我是ocaml新手,因此为这个愚蠢的问题表示歉意。我的Google技术使我失败了!
我正在尝试执行一系列let绑定,并希望在for循环中执行它们(我知道,我知道)。当我在for循环中放置let绑定时,“完成;”成为语法错误。我认为我缺少明显的东西。欢迎以下MWE的其他任何指针:
module PF = Map.Make(struct type t = int let compare = compare end);;
module Factorize = Map.Make(struct type t = int let compare = compare end);;
let a = PF.empty;;
let a = PF.add 2 5 a;;
let a = PF.add 3 3 a;;
let b = PF.empty;;
let b = PF.add 3 1 b;;
let b = PF.add 5 2 b;;
let print_map = PF.iter (fun a b -> Printf.printf "%d %d\n" a b);;
print_map a;;
print_map b;;
(*THESE LET BINDINGS WORK*)
let primeFactors = Factorize.empty;;
let primeFactors = Factorize.add 1 (PF.add 1 3 PF.empty) primeFactors;;
let primeFactors = Factorize.add 2 (PF.add 2 3 PF.empty) primeFactors;;
let primeFactors = Factorize.add 3 (PF.add 3 3 PF.empty) primeFactors;;
(*BUT WE CANNOT DO A BUNCH OF THEM IN A LOOP*)
for k = 4 to 20 do
let primeFactors = Factorize.add k (PF.add k 3 PF.empty) primeFactors;
done;; (* THIS LINE FAILS*)
严格来说,这不是MWE,但想法是有两个映射。第一个将int映射到int,第二个将int映射到PF映射。
let绑定本身似乎起作用。但是以某种方式进行let绑定会导致操作失败。我的猜测是应该返回一个(),但我不知道如何使它执行该操作。
答案 0 :(得分:3)
起初这可能有点令人困惑。 let
关键字在顶层的功能不同于在表达式上下文中的功能。
第一组let
绑定位于模块的顶层。顶级绑定的语法为let x = expr
。这就是for
循环之前的let绑定中的内容(除非您在交互式顶层中使用,否则不需要结尾;;
,但它们不会对您造成任何伤害)。
但是,在任何其他上下文中(例如,在您的for
循环中),let
绑定将变量in
绑定到本地作用域。语法为let x = expr1 in expr2
-注意in
。 in
将变量(此处为x
与值expr1
)的绑定与该绑定所保持的以下作用域(此处为expr2
)分开。
例如,
for k = 4 to 20 do
let v = k * k in
print_endline (string_of_int v)
done
请注意,使用ref
单元格进行命令式编程时,不需要let绑定来更新ref
单元格的值:
let v = ref 0 in
for k = 4 to 20 do
v := !v + k
done;
print_endline (string_of_int !v);
(* => 204 *)
如果这不清楚或您有任何后续问题,请告诉我!
答案 1 :(得分:2)
TL; DR; let
不是工作分配:=
。让我们创建一个词法绑定,它不更改任何值。
首先,您将分配与阴影混淆了。
完成时
let x = 42
let x = 52
您没有更改x
的值,而是将变量x
绑定到新值52
。在OCaml中,变量纯粹是数学的,从某种意义上说,它们不充当存储单元,也不在其内部存储任何内容。它们只是名称,程序员使用它们来引用表达式的结果。
此外,您甚至可以将不同类型的值绑定到同一标识符,例如
let x = 42
let x = "hello"
现在,要创建参考单元格(可以将其作为可以存储值的对象),可以使用ref
函数。它将返回一个盒子,上面有一个可以存放的地方
您的价值观。
let x = ref 42
,要更改此单元格中存储的值,您必须使用(:=)
函数,例如
x := 56
现在,存储在我们名为x
的单元格中的值是56
,要检索此值,必须使用(!)
函数。例如,以下内容将使存储在单元格中的值翻倍:
x := !x * !x
您可以循环执行此操作
for i = 0 to 9 do
x := !x * !x
done
现在,让我们看看您做了什么:)
let primeFactors = ref Factorize.empty;;
您创建一个包含空映射的单元格,并将其绑定到primeFactors
名称。绑定到primeFactors
变量的值具有类型Factorize.t ref
,即对Factorize.t
的引用。到目前为止一切顺利!
let primeFactors = Factorize.add 1 (PF.add 1 3 PF.empty) primeFactors;;
首先,它不会编译,因为Factorize.add
期望Factorize.t
,但是您要给它指定类型Factorize.t ref
的值。虽然,从语法上讲它是正确的,但它不是有效的程序。但是,即使我们可以通过使用(!)
函数将单元格解引用为它的值来使它具有良好的类型,
let primeFactors = Factorize.add 1 (PF.add 1 3 PF.empty) !primeFactors
它仍然是错误的程序(并非所有键入正确的程序都是正确的!)。
我们在这里所做的是,将primeFactors
变量反弹到一个新值,该新值是从1
到(PF.add 1 3 PF.empty)
值的映射。因此,我们更改了与primeFactors
变量关联的值及其类型。我们应该做的是
primeFactors := Factorize.add 1 (PF.add 1 3 PF.empty) !primeFactors