Tree F#表达式树解释器的实现

Tree F#表达式树解释器的实现,tree,f#,Tree,F#,我正试图为一个表达式树构造一个解释器,它可以在F#中进行加法或乘法,但我遇到了一些麻烦。我还尝试使用选项类型,这样如果变量不在表达式中,解释器将返回None 这是给我的代码 type Exptree = | Const of int | Var of string | Add of Exptree * Exptree | Mul of Exptree * Exptree type Bindings = (string * int) list let rec eval(exp

我正试图为一个表达式树构造一个解释器,它可以在F#中进行加法或乘法,但我遇到了一些麻烦。我还尝试使用选项类型,这样如果变量不在表达式中,解释器将返回None

这是给我的代码

type Exptree =
  | Const of int
  | Var of string
  | Add of Exptree * Exptree
  | Mul of Exptree * Exptree

type Bindings = (string * int) list

let rec eval(exp : Exptree, env:Bindings) =  
  match exp with
  |  
我不知道该用什么来匹配exp,因为有太多的选项。我的想法是查看Add和Mul,并尝试在每个递归步骤中剥离它,直到得到一个整数,但是我完全迷路了

我能做些类似的事情吗

match exp with
| Some(Add) -> int + int?
| Some(Mul) -> int * int?

Exptree类型有四个可能的构造函数:Const、Var、Add和Mul。所以,你的对手应该是:

match exp with
| Const c -> 
| Var s ->
| Add (e1,e2) -> 
| Mul (e1,e2) ->

对于这些情况中的每一种,您都需要执行适当的操作(添加、乘法、在环境中查找变量、返回常量)。在add和mul情况下,您将解释子表达式(e1和e2)上递归调用eval的结果,以查看值是否为None或Some,并相应地对其进行操作。

您的Exptree类型有四个可能的构造函数:Const、Var、add和mul。所以,你的对手应该是:

match exp with
| Const c -> 
| Var s ->
| Add (e1,e2) -> 
| Mul (e1,e2) ->

对于这些情况中的每一种,您都需要执行适当的操作(添加、乘法、在环境中查找变量、返回常量)。在add和mul情况下,您将解释子表达式(e1和e2)上递归调用eval的结果,以查看值是否为None或Some,并相应地对其进行操作。

因此,scrwtp是正确的,
Map
对您的绑定集更有意义,因为您可以在O(logn)时间内执行查找,而不是使用
列表的O(n)时间

eval
功能的第一部分很简单:

  • 如果该值是一个常量,我们只需要返回该常量的
    部分
  • 如果该值是一个变量,我们需要在映射中查找它,如果键存在,我们将返回该值的
    Some
    。如果它们的键不存在,我们将返回
    None
    。我们可以使用
    Map.tryFind
    来实现这一点
  • 我们可以这样做:

    let rec eval bindings tree =
        match tree with
        |Const i -> Some i
        |Var varName -> Map.tryFind varName bindings
    
    执行加法和乘法需要更多的思考。为了抽象合并两个子树,有必要定义一个函数,该函数将两个选项作为参数,并允许您将一个函数应用于两个结果(如果它们都是某个值)。这称为
    lift2
    函数,我们可以为
    选项定义它:

    /// Combines two option values using a supplied function
    let lift2 f opt1 opt2 =
        match (opt1, opt2) with
        |Some val1, Some val2 -> Some <| f val1 val2
        |_ -> None
    
    现在,add case使用
    lift2
    函数表示,如果两个子表达式的计算结果都是实值,则将结果相加,否则返回
    None

    乘法情况遵循完全相同的逻辑,我们刚刚交换了运算符

    一个简短的旁白:正如GuyCoder所指出的,构建绑定表的一个方便方法是使用以下语法:

    let bindings = Map.ofList [("a",1); ("b",2); ("c",3)]
    

    因此,scrwtp是正确的,
    Map
    对您的绑定集更有意义,因为您可以在O(logn)时间内执行查找,而不是使用
    list
    在O(n)时间内执行查找

    eval
    功能的第一部分很简单:

  • 如果该值是一个常量,我们只需要返回该常量的
    部分
  • 如果该值是一个变量,我们需要在映射中查找它,如果键存在,我们将返回该值的
    Some
    。如果它们的键不存在,我们将返回
    None
    。我们可以使用
    Map.tryFind
    来实现这一点
  • 我们可以这样做:

    let rec eval bindings tree =
        match tree with
        |Const i -> Some i
        |Var varName -> Map.tryFind varName bindings
    
    执行加法和乘法需要更多的思考。为了抽象合并两个子树,有必要定义一个函数,该函数将两个选项作为参数,并允许您将一个函数应用于两个结果(如果它们都是某个值)。这称为
    lift2
    函数,我们可以为
    选项定义它:

    /// Combines two option values using a supplied function
    let lift2 f opt1 opt2 =
        match (opt1, opt2) with
        |Some val1, Some val2 -> Some <| f val1 val2
        |_ -> None
    
    现在,add case使用
    lift2
    函数表示,如果两个子表达式的计算结果都是实值,则将结果相加,否则返回
    None

    乘法情况遵循完全相同的逻辑,我们刚刚交换了运算符

    一个简短的旁白:正如GuyCoder所指出的,构建绑定表的一个方便方法是使用以下语法:

    let bindings = Map.ofList [("a",1); ("b",2); ("c",3)]
    

    这与你的问题无关,但是这个
    绑定
    类型作为
    映射
    更有意义。这与你的问题无关,但是这个
    绑定
    类型作为
    映射
    更有意义。细节不错。由于
    bindings
    更改为
    Map
    您可能还需要注意如何为新的F#用户构建它们,例如
    let bindings=Map.ofList[((“a”,1);(“b”,2);(“c”,3)]
    @GuyCoder这主意不错,我已经提出了建议的添加。细节不错。由于
    bindings
    更改为
    Map
    您可能还需要注意如何为新的F#用户构造它们,例如
    let bindings=Map.ofList[((“a”,1);(“b”,2);(“c”,3)]
    @GuyCoder这是个不错的主意,我提出了建议的添加。