Gần đây tôi đã tham dự một khóa học trực tuyến về các ngôn ngữ lập trình, trong đó, trong số các khái niệm khác, việc đóng cửa đã được trình bày. Tôi viết ra hai ví dụ lấy cảm hứng từ khóa học này để đưa ra một số bối cảnh trước khi đặt câu hỏi của tôi.
Ví dụ đầu tiên là hàm SML tạo ra danh sách các số từ 1 đến x, trong đó x là tham số của hàm:
fun countup_from1 (x: int) =
let
fun count (from: int) =
if from = x
then from :: []
else from :: count (from + 1)
in
count 1
end
Trong REPL SML:
val countup_from1 = fn : int -> int list
- countup_from1 5;
val it = [1,2,3,4,5] : int list
Các countup_from1
chức năng sử dụng việc đóng cửa helper count
mà chụp và sử dụng biến x
từ ngữ cảnh của nó.
Trong ví dụ thứ hai, khi tôi gọi một hàm create_multiplier t
, tôi lấy lại một hàm (thực ra là một bao đóng) nhân số đối số của nó bằng t:
fun create_multiplier t = fn x => x * t
Trong REPL SML:
- 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
Vì vậy, biến m
bị ràng buộc với bao đóng được trả về bởi lệnh gọi hàm và bây giờ tôi có thể sử dụng nó theo ý muốn.
Bây giờ, để việc đóng hoạt động đúng trong suốt vòng đời của nó, chúng ta cần kéo dài thời gian tồn tại của biến bị bắt t
(trong ví dụ này là số nguyên nhưng nó có thể là giá trị của bất kỳ loại nào). Theo như tôi biết, trong SML, điều này có thể được thực hiện bằng cách thu gom rác: việc đóng cửa giữ một tham chiếu đến giá trị bị bắt mà sau đó được người thu gom xử lý khi đóng cửa bị phá hủy.
Câu hỏi của tôi: nói chung, việc thu gom rác là cơ chế duy nhất có thể để đảm bảo rằng việc đóng cửa an toàn (có thể gọi được trong suốt cuộc đời của họ)?
Hoặc các cơ chế khác có thể đảm bảo tính hợp lệ của các bao đóng mà không cần thu gom rác: Sao chép các giá trị đã chụp và lưu trữ bên trong bao đóng? Hạn chế thời gian đóng của chính nó để nó không thể được gọi sau khi các biến bị bắt của nó đã hết hạn?
Các phương pháp phổ biến nhất là gì?
BIÊN TẬP
Tôi không nghĩ rằng ví dụ trên có thể được giải thích / thực hiện bằng cách sao chép (các) biến đã chụp vào bao đóng. Nói chung, các biến bị bắt có thể thuộc bất kỳ loại nào, ví dụ: chúng có thể được liên kết với một danh sách rất lớn (không thay đổi). Vì vậy, trong quá trình thực hiện, việc sao chép các giá trị này sẽ rất không hiệu quả.
Để hoàn thiện, đây là một ví dụ khác sử dụng tài liệu tham khảo (và tác dụng phụ):
(* 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 ();
Trong REPL SML:
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
Vì vậy, các biến cũng có thể được ghi lại bằng tham chiếu và vẫn còn sống sau khi lệnh gọi hàm đã tạo chúng ( create_counter ()
) đã hoàn thành.