ฉันเพิ่งเข้าร่วมหลักสูตรออนไลน์เกี่ยวกับภาษาการเขียนโปรแกรมซึ่งมีการนำเสนอการปิด ฉันเขียนสองตัวอย่างที่ได้รับแรงบันดาลใจจากหลักสูตรนี้เพื่อให้บริบทก่อนถามคำถาม
ตัวอย่างแรกคือฟังก์ชัน 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 ()
) เสร็จสมบูรณ์