OCaml: внутреннее «let» для двух функций - PullRequest
0 голосов
/ 18 мая 2018

Я хочу сделать внутреннее «пусть», но для двух функций.

Теперь у меня есть функция

let fresh_var () =
let r = ref 0 in
r := !r + 1 ; Var !r;;

Я хочу добавить вторую функцию, чтобы она могла изменять r, но r остается невидимой для остальной части программы.Что-то вроде:

let r = ref 0 in 
let fresh_var () = r := !r + 1 ; Var !r
and let refresh () = r := 0

Но фрагмент выше не работает из-за синтаксической ошибки.

Как я могу реализовать эту идею в OCaml?

Ответы [ 4 ]

0 голосов
/ 19 мая 2018

OCaml также поддерживает богатую объектно-ориентированную систему

class counter = object
  val mutable r = 0

  method value =
    r

  method incr =
    r <- r + 1;
    r

  method reset =
    r <- 0;
    0
end

Мы можем использовать наши counter как этот

let () =
  let c = new counter in
  printf "counter value: %d\n" c#value;   (* counter value: 0 *)
  printf "counter value: %d\n" c#incr;    (* counter value: 1 *)
  printf "counter value: %d\n" c#incr;    (* counter value: 2 *)
  printf "counter value: %d\n" c#incr;    (* counter value: 3 *)
  printf "counter value: %d\n" c#value;   (* counter value: 3 *)
  printf "counter value: %d\n" c#value;   (* counter value: 3 *)
  printf "counter value: %d\n" c#reset;   (* counter value: 0 *)
  printf "counter value: %d\n" c#incr;    (* counter value: 1 *)

Экземпляры класса инкапсулируют их элементы данных, поэтому мы можем легкоуправлять несколькими счетчиками

let () =
  let a = new counter in
  let b = new counter in
  printf "A: %d, B: %d\n" a#value b#value;  (* A: 0, B: 0 *)
  printf "A: %d, B: %d\n" a#incr b#value;   (* A: 1, B: 0 *)
  printf "A: %d, B: %d\n" a#incr b#value;   (* A: 2, B: 0 *)
  printf "A: %d, B: %d\n" a#incr b#value;   (* A: 3, B: 0 *)
  printf "A: %d, B: %d\n" a#value b#incr;   (* A: 3, B: 1 *)
  printf "A: %d, B: %d\n" a#value b#incr;   (* A: 3, B: 2 *)
  printf "A: %d, B: %d\n" a#value b#incr;   (* A: 3, B: 3 *)
  printf "A: %d, B: %d\n" a#reset b#reset;  (* A: 0, B: 0 *)
0 голосов
/ 18 мая 2018

Это не так, как все работает в OCaml. Локальные определения являются именно такими, локальными.Они не могут совместно использоваться функциями.

Если вы хотите абстрагировать часть своей реализации, я предлагаю вместо этого использовать модули.

module Incrementer : sig
  val next : unit -> int
  val reset : unit -> unit
end = struct
  let r = ref 0

  let next () =
    r := !r + 1;
    !r

  let reset () =
    r := 0
end

См. Это в действии ниже:

# Incrementer.next ();;
- : int = 1

# Incrementer.next ();;
- : int = 2

# Incrementer.reset ();;
- : unit = ()

# Incrementer.next ();;
- : int = 1

# Incrementer.r;;
Error: Unbound value Incrementer.r

Ниже приведена лучшая реализация, которая позволяет вам иметь несколько Incrementers одновременно.

module Incrementer : sig
  type t
  val create : unit -> t
  val next : t -> int
  val reset : t -> unit
end = struct
  type t = int ref

  let create () =
    ref 0

  let next t =
    t := !t + 1;
    !t

  let reset t =
    t := 0
end

Давайте посмотрим на это в действии:

# let incrementer = Incrementer.create ();;
val incrementer : Incrementer.t = <abstr>
(* As you can see, the outer code never sees the `int ref` inside. *)

# Incrementer.next incrementer;;
- : int = 1

# Incrementer.next incrementer;;
- : int = 2

# Incrementer.reset incrementer;;
- : unit = ()

# Incrementer.next incrementer;;
- : int = 1

Вы также можете написать подпись и реализацию в отдельных файлах, чтобы скомпилировать их отдельно.

0 голосов
/ 18 мая 2018

Вы можете просто создать одну функцию, возвращающую пару функций:

let get_fresh_and_reset () =
  let r = ref 0 in
  (fun () -> incr r; !r), (fun () -> r := 0)

let fresh, reset = get_fresh_and_reset ()

Также обратите внимание, что правильный синтаксис - :=, а не =:.

Редактировать:

Как упоминалось @ Virgile , если вам не нужно несколько счетчиков, вы можете упростить:

let fresh_var, refresh =
    let r = ref 0 in (fun () -> incr r; !r), (fun () -> r:=0)
0 голосов
/ 18 мая 2018

and используется вместо let, когда вы хотите написать совместные объявления, а in используется только в локальном контексте:

let r = ref 0
let fresh_var () = r := !r + 1 ; Var !r
and refresh () = r := 0

Однако, поскольку ваши функции не зависят от кода,использование and здесь не является необходимым, поэтому вы можете использовать другую конструкцию let.

Что касается вашей идеи, вам придется определять эти функции в отдельном модуле, чей .mli только объявляет иха не переменная r.

...