OCaml:内部;让我们;有两个功能

OCaml:内部;让我们;有两个功能,ocaml,let,Ocaml,Let,我想制作内部的“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中实现这一想法?

我想制作内部的“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中实现这一想法?

在您想要编写联合声明时使用而不是
let
,而中的
仅在本地上下文中使用:

let r = ref 0
let fresh_var () = r := !r + 1 ; Var !r
and refresh () = r := 0
但是,由于您的函数不是相互依赖的,因此此处不需要使用
,因此您可以使用另一个
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

下面是一个更好的实现,它允许您同时拥有多个
递增器

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
#让incrementer=incrementer.create();;
val incrementer:incrementer.t=
(*如您所见,外部代码从未在内部看到'int ref'。*)
#Incrementer.next Incrementer;;
-:int=1
#Incrementer.next Incrementer;;
-:int=2
#Incrementer.reset Incrementer;;
-:单位=()
#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 ()
还请注意,正确的语法是
:=
,而不是
=:

编辑:

如前所述,如果您不需要几个计数器,您可以简化:

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
类实例封装了它们的数据成员,因此我们可以轻松地管理多个计数器

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

如果
.mli
未声明
r
从程序的其余部分看不见,则这并不能满足OP的要求,即“r在程序的其余部分保持不可见”。除非所述程序包含在单个模块中,或者甚至是一个脚本被提供给解释器。这是真的,但我不明白你是如何推断这是一个约束的?我不知道在这种特殊情况下这是否是一个约束。我的观点基本上与Vonaka在其答案下的评论相同:对于这样一个简单的情况,定义一对共享局部变量的函数要比定义整个
模块
.mli
路径容易得多。这允许您定义几个独立的计数器,但万一只有一个计数器,你可以直接做
let fresh\u var,refresh=let r=ref 0 in(fun()->incr;!r),(fun()->r:=0)
这感觉很粗糙,但为什么不呢:p这有真实世界的应用程序,还是仅仅是语言的一种怪癖?@Richard Degenne,我不知道真实世界,但我不觉得这是一种黑客行为。显然,如果您需要一些全局计数器结构,最好使用模块,但当它只是一个小的内部计数器时,我看不出有任何理由不选择此解决方案。您还可以使用命名函数,使其在代码较长时更具可读性:
let(fresh_var,refresh)=let r=ref 0 in let fresh_var()=incr!r-in-let-refresh()=r:=0-in(fresh\u-var,refresh)
。这种构造更一般的用途是为记录字段编写一对getter/setter函数(引用只是一个可变记录),以便将它们传递给不知道该记录的多态函数。
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 *)