非常接近让这个工作,但从OCaml的StringMap遇到麻烦。本质上我正在制作一个计算器,它从ocamllex中获取词汇流...所以这里的逗号应该分离出我们的表达式,而等号表示我们将为变量赋值。
我意识到,在分配变量时,我无法查找它们,因为我得到了(致命错误:异常Not_found),因为没有找到我在函数的Var情况下添加的密钥。我不知道在哪里放置StringMap.empty或如何使它在这个函数中可见...我想知道为什么它无法找到我在equals情况下添加的内容?
这是我的代码。
open Ast
module StringMap = Map.Make(String)
let varMap = StringMap.empty
let rec parser = function
Lit(x) -> x
| Binop(e1, op, e2) -> (
let v1 = parser e1 and v2 = parser e2 in
match op with
Add -> v1 + v2
| Sub -> v1 - v2
| Mul -> v1 * v2
| Div -> v1 / v2
)
| Var(v) -> StringMap.find v varMap
| Statements(e1, e2) -> ignore(parser e1); parser e2
| Equals(v, e1) -> StringMap.add v e1 varMap; parser e1
let _ =
let LexingBuffer = Lexing.from_channel stdin in
let expression = Parser.expression Scanner.token LexingBuffer in
let result = parser expression in
print_endline (string_of_int result)
Map
是一个不可变的数据结构。任何更改地图的函数都将返回一个新的修改过的实例,同时保留前一个不变的实例。所以,这个表达方式
StringMap.add v e1 varMap; parser e1
只会扔掉新地图,然后返回parser
返回的递归调用。如果你想使用Map
,你必须保持新的实例。您可以通过将varMap
更新为您更新的ref单元格来执行此操作:
let varMap = ref StringMap.empty
...
varMap := StringMap.add v1 !varMap
或者通过向parser
添加一个函数参数并将其传递给:
let rec parser varMap = function
...
parser (StringMap.add v e1 varMap) e1
另一种选择是使用Hashtbl,它是可变的并且对字符串非常有效。
地图是不可变的。这条线
StringMap.add v e1 varMap; parser e1
因此,计算更新的地图,然后立即丢弃它。换句话说,地图varMap
总是空的。
为避免丢弃环境贴图,您应该在函数parser
中跟踪它,方法是将其添加为函数的参数 - 您可以将其重命名为eval
:
let rec eval env = function
| ...