我最近参加了有关编程语言的在线课程,其中介绍了闭包。我写下了两个受本课程启发的示例,以便在提出问题之前提供一些背景信息。
第一个示例是一个SML函数,该函数生成从1到x的数字列表,其中x是该函数的参数:
fun countup_from1 (x: int) =
let
fun count (from: int) =
if from = x
then from :: []
else from :: count (from + 1)
in
count 1
end
在SML REPL中:
val countup_from1 = fn : int -> int list
- countup_from1 5;
val it = [1,2,3,4,5] : int list
该countup_from1
函数使用帮助程序闭包count
,该闭包x
从其上下文捕获并使用变量。
在第二个示例中,当我调用一个函数时create_multiplier t
,我得到了一个将其参数乘以t的函数(实际上是一个闭包):
fun create_multiplier t = fn x => x * t
在SML REPL中:
- fun create_multiplier t = fn x => x * t;
val create_multiplier = fn : int -> int -> int
- val m = create_multiplier 10;
val m = fn : int -> int
- m 4;
val it = 40 : int
- m 2;
val it = 20 : int
因此,变量m
绑定到函数调用返回的闭包上,现在我可以随意使用它了。
现在,为了使闭包在其整个生命周期内正常工作,我们需要延长捕获变量的生命周期t
(在示例中,它是整数,但可以是任何类型的值)。据我所知,在SML中,这可以通过垃圾回收来实现:闭包保留对捕获值的引用,该值随后在销毁闭包时由垃圾回收器处理。
我的问题:总的来说,垃圾回收是确保关闭安全(在整个生命周期中都可以调用)的唯一可能机制吗?
还是有什么其他机制可以确保不进行垃圾收集而关闭的有效性:复制捕获的值并将其存储在关闭中?限制闭包本身的生存期,以便在其捕获的变量到期后不能调用它?
最受欢迎的方法是什么?
编辑
我不认为可以通过将捕获的变量复制到闭包中来解释/实现上述示例。通常,捕获的变量可以是任何类型,例如,它们可以绑定到非常大的(不可变的)列表。因此,在实现中,复制这些值将非常低效。
为了完整起见,这是另一个使用引用(和副作用)的示例:
(* Returns a closure containing a counter that is initialized
to 0 and is incremented by 1 each time the closure is invoked. *)
fun create_counter () =
let
(* Create a reference to an integer: allocate the integer
and let the variable c point to it. *)
val c = ref 0
in
fn () => (c := !c + 1; !c)
end
(* Create a closure that contains c and increments the value
referenced by it it each time it is called. *)
val m = create_counter ();
在SML REPL中:
val create_counter = fn : unit -> unit -> int
val m = fn : unit -> int
- m ();
val it = 1 : int
- m ();
val it = 2 : int
- m ();
val it = 3 : int
因此,变量也可以通过引用捕获,并且在创建变量的函数调用(create_counter ()
)完成后仍然有效。