позвольте заявлению внутри ocaml для цикла - PullRequest
0 голосов
/ 21 марта 2019

Я ocaml noob, так что извиняюсь за глупый вопрос.Мои навыки Google подвели меня!

Я пытаюсь сделать серию привязок let и хочу сделать их в цикле for (я знаю, я знаю).Когда я помещаю привязку let в цикл for, "done ;;"становится синтаксической ошибкой.Я предполагаю, что упускаю что-то очевидное.Любые другие указатели для следующего 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, а второй - с отображением PF.

Кажется, что само связывание let работает.Но каким-то образом наличие привязки let приводит к неудаче.Я предполагаю, что он должен возвращать (), но я не мог понять, как заставить это сделать это.

Ответы [ 2 ]

3 голосов
/ 21 марта 2019

Поначалу это может немного сбить с толку. Ключевое слово let работает на верхнем уровне иначе, чем в контексте выражения.

Первая группа let привязок находится на верхнем уровне вашего модуля. Синтаксис для привязок верхнего уровня: let x = expr. Это то, что у вас есть в привязках let, предшествующих циклу for (завершающий ;; не нужен, если вы не работаете на интерактивном верхнем уровне, но они ничего не повредят).

Однако в любом другом контексте (например, в вашем цикле 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 *)

Пожалуйста, дайте мне знать, если это кажется неясным или у вас есть дополнительные вопросы!

2 голосов
/ 21 марта 2019

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 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...