Tree F#表达式树解释器的实现
我正试图为一个表达式树构造一个解释器,它可以在F#中进行加法或乘法,但我遇到了一些麻烦。我还尝试使用选项类型,这样如果变量不在表达式中,解释器将返回None 这是给我的代码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
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这是个不错的主意,我提出了建议的添加。