因此,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