OCaml:内部;让我们;有两个功能
我想制作内部的“let”,但有两个函数 我现在有一个函数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 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 *)