OCaml:两个函数的内部 "let"

OCaml: inner "let" for two functions

我想制作内部 "let",但有两个功能。

我现在有一个功能

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 中实现这个想法?

and是用来代替let的,当你要写联合声明的时候,in只在局部上下文中使用:

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

但是,由于您的函数不是相互依赖的,因此此处不需要使用 and,因此您可以使用另一个 let 结构。

至于你的想法,你必须在一个单独的模块中定义这些函数,该模块的 .mli 只声明它们而不是变量 r.

这不是 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

您也可以将签名和实现写在单独的文件中,以便单独编译它们。

您可以简单地创建一个返回一对函数的函数:

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)

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 *)

Class实例封装了自己的数据成员,方便我们管理多个计数器

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 *)