Kısa süre önce, diğer kavramların yanı sıra kapakların sunulduğu programlama dilleri üzerine çevrimiçi bir kursa katıldım. Sorumu sormadan önce bu bağlamdan ilham almak için iki örnek yazıyorum.
İlk örnek, 1'den x'e kadar olan sayıların listesini üreten bir SML işlevidir; burada x, işlevin parametresidir:
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'de:
val countup_from1 = fn : int -> int list
- countup_from1 5;
val it = [1,2,3,4,5] : int list
countup_from1
Fonksiyon yardımcı kapatılması kullandığı count
o yakalar ve kullanımları değişkeni x
onun bağlamdan.
İkinci örnekte, bir işlevi çağırdığımda create_multiplier t
, argümanını t ile çarpan bir işlevi (aslında bir kapatma) geri alıyorum:
fun create_multiplier t = fn x => x * t
SML REPL'de:
- 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
Değişken m
, işlev çağrısı tarafından döndürülen kapatmaya bağlıdır ve şimdi bunu isteğe bağlı olarak kullanabilirim.
Şimdi, kapağın kullanım ömrü boyunca düzgün çalışması için, yakalanan değişkenin ömrünü uzatmamız gerekir t
(örnekte bir tamsayıdır, ancak her türden bir değer olabilir). Bildiğim kadarıyla, SML'de bu çöp toplama ile mümkün olur: Kapak, daha sonra kapak imha edildiğinde çöp toplayıcı tarafından atılan yakalanan değere bir referans tutar.
Benim sorum: Genel olarak, çöp toplama, kapakların güvenli olmasını sağlamak için mümkün olan tek mekanizma mıdır (tüm kullanım ömrü boyunca çağrılabilir)?
Veya çöp toplama olmadan kapakların geçerliliğini sağlayabilecek diğer mekanizmalar nelerdir: Yakalanan değerleri kopyalayın ve kapağın içinde saklayın? Kapanmanın ömrünü, yakalanan değişkenlerinin süresi dolduktan sonra çağrılamayacak şekilde kısıtlansın mı?
En popüler yaklaşımlar nelerdir?
DÜZENLE
Yukarıdaki örneğin yakalanan değişken (ler) i kapatılarak kopyalanarak açıklanabileceğini / uygulanabileceğini sanmıyorum. Genel olarak, yakalanan değişkenler herhangi bir tipte olabilir, örneğin çok büyük (değişmez) bir listeye bağlanabilirler. Dolayısıyla, uygulamada bu değerleri kopyalamak çok verimsiz olacaktır.
Tamlık uğruna, referansları (ve yan etkileri) kullanan başka bir örnek:
(* 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'de:
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
Bu nedenle, değişkenler referans olarak da yakalanabilir ve onları ( create_counter ()
) oluşturan işlev çağrısı tamamlandıktan sonra da canlı kalır.