因此,scrwtp是正确的,Map<string, int>
对于您的一组绑定更有意义,因为您可以在O(log n)时间内执行查找,而不是使用list
进行O(n)时间查找。
的eval
功能的第一部分很简单:
- 如果该值是恒定的,我们只需要返回不变的
Some
。
- 如果该值是一个变量,我们需要在地图上查找它,如果该键存在,我们返回值为
Some
。如果他们的钥匙不存在,我们将返回None
。我们可以使用Map.tryFind
来做到这一点。
我们能做到这一点的部分是这样的:
let rec eval bindings tree =
match tree with
|Const i -> Some i
|Var varName -> Map.tryFind varName bindings
执行加法和乘法需要一些更多的思考。为了抽象地合并两个子树,定义一个函数将两个选项作为参数是有意义的,并且如果它们都是Some value
,则可以将函数应用于两个结果。这就是所谓的lift2
功能,我们可以将其定义为Option
:
/// 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
欲了解更多的lift
家庭的功能,看到这个伟大的教程:http://fsharpforfunandprofit.com/posts/elevated-world/#lift
现在我们可以扩展我们eval
功能的加法并通过调用这个函数来乘法情况。现在
/// Evaluates the result of an expression tree
let rec eval bindings tree =
match tree with
|Const i -> Some i // Const always returns some value
|Var varName -> Map.tryFind varName bindings // Returns some value if it exists in the binding table
|Add (tree1, tree2) ->
lift2 (+) (eval bindings tree1) (eval bindings tree2) // add expressions if both expressions return Some val
|Mul (tree1, tree2) ->
lift2 (*) (eval bindings tree1) (eval bindings tree2) // multiply expressions if both expressions return Some val
,添加的情况下使用lift2
功能地说,如果两个子表达式计算为一个真正的价值,结果加在一起,否则返回None
。
乘法情况遵循完全相同的逻辑,我们只是交换操作符。
快速旁白:作为GuyCoder指出,建立自己的绑定表的便捷方式将被使用的语法:
let bindings = Map.ofList [("a",1); ("b",2); ("c",3)]
那是你的问题的旁边,但这个'Bindings'类型使得作为'Map'更有意义。 –
scrwtp