在ocaml for循环内的let语句

时间:2019-03-21 18:14:51

标签: for-loop ocaml

我是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绑定会导致操作失败。我的猜测是应该返回一个(),但我不知道如何使它执行该操作。

2 个答案:

答案 0 :(得分:3)

起初这可能有点令人困惑。 let关键字在顶层的功能不同于在表达式上下文中的功能。

第一组let绑定位于模块的顶层。顶级绑定的语法为let x = expr。这就是for循环之前的let绑定中的内容(除非您在交互式顶层中使用,否则不需要结尾;;,但它们不会对您造成任何伤害)。

但是,在任何其他上下文中(例如,在您的for循环中),let绑定将变量in绑定到本地作用域。语法为let x = expr1 in expr2-注意inin将变量(此处为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 
相关问题