2012-03-01 35 views
6

Tôi đã thấy mã sau ... Cuộc gọi đầu tiên của (next-num) trả về 1 và số thứ hai trả về 2.Biến trong một hàm

(define next-num 
    (let ((num 0)) 
    (lambda() (set! num (+ num 1)) num))) 

(next-num) ; 1 
(next-num) ; 2 

Những gì tôi không thể hiểu là ... num được tạo ra bởi let bên next-num, nó là loại một biến địa phương ... Làm thế nào để chương trình biết rằng mỗi lần next-num được gọi là, giá trị của num là không bị xóa bởi let ((num 0)); Chương trình này biết rằng nó luôn giống như num mà chúng tôi sửa đổi bất cứ khi nào next-num được gọi?

Dường như num là cả địa phương và tĩnh ... Làm cách nào chúng tôi có thể xác định biến cục bộ, nhưng không tĩnh?

Trả lời

9

Đây là "đóng cửa từ vựng" và bạn đúng là num, "biến đóng trên" tương tự như biến tĩnh, trong C ví dụ: chỉ hiển thị mã trong biểu mẫu let phạm vi "), nhưng nó vẫn tồn tại trên toàn bộ chương trình chạy, thay vì được khởi tạo lại với mỗi cuộc gọi đến hàm.

Tôi nghĩ rằng phần bạn đang bối rối là: "num được tạo bởi cho phép bên trong next-num, nó là loại biến cục bộ". Điều này không đúng bởi vì khối let không phải là một phần của hàm next-num: đó thực sự là một biểu thức tạo và trả về hàm sau đó được liên kết với next-num. (Điều này rất khác, ví dụ, từ C, trong đó các hàm chỉ có thể được tạo tại thời gian biên dịch và bằng cách định nghĩa chúng ở mức cao nhất. Trong Đề án, các hàm là các giá trị như số nguyên hoặc danh sách, bất kỳ biểu thức nào có thể trả về).

Dưới đây là một cách khác để viết (gần như) điều tương tự mà làm cho nó rõ ràng hơn rằng define chỉ được liên kết next-num với giá trị của một biểu thức hàm trả về:

(define next-num #f) ; dummy value 
(let ((num 0)) 
    (set! next-num 
     (lambda() (set! num (+ num 1)) num))) 

Điều quan trọng cần lưu ý sự khác biệt giữa

(define (some-var args ...) expression expression ...) 

mà làm some-var một chức năng mà thực thi tất cả expressions khi gọi, và

(define some-var expression) 

liên kết some-var với giá trị expression, được đánh giá rồi đến đó. Nói đúng ra, các phiên bản cũ là không cần thiết, bởi vì nó tương đương với

(define some-var 
    (lambda (args ...) expression expression ...)) 

Mã của bạn là gần như giống nhau như thế này, với việc bổ sung các biến lexically scoped, num, xung quanh hình thức lambda.

Cuối cùng, đây là điểm khác biệt chính giữa các biến đóng và biến tĩnh, làm cho các bao đóng mạnh hơn rất nhiều.Nếu bạn đã viết những điều sau đây thay vì:

(define make-next-num 
    (lambda (num) 
    (lambda() (set! num (+ num 1)) num))) 

sau đó mỗi cuộc gọi đến make-next-num sẽ tạo ra một chức năng ẩn danh với một mới, khác biệt num biến, đó là tới chức năng:

(define f (make-next-num 7)) 
(define g (make-next-num 2)) 

(f) ; => 8 
(g) ; => 3 
(f) ; => 9 

Đây là một lừa thực sự thú vị và mạnh mẽ, chiếm rất nhiều sức mạnh của ngôn ngữ với các đóng cửa từ vựng.

Đã chỉnh sửa để thêm: Bạn hỏi cách Scheme "biết" num nào để sửa đổi khi gọi next-num. Trong phác thảo, nếu không thực hiện, điều này thực sự khá đơn giản. Mỗi biểu thức trong Đề án được đánh giá trong ngữ cảnh của một môi trường (bảng tra cứu) của các ràng buộc biến, là các kết hợp của các tên tới các địa điểm có thể chứa các giá trị. Mỗi đánh giá của một hình thức let hoặc một cuộc gọi chức năng tạo ra một môi trường mới bằng cách mở rộng môi trường hiện tại với các ràng buộc mới. Để sắp xếp để có các biểu mẫu lambda hoạt động như các bao đóng, việc triển khai biểu diễn chúng dưới dạng một cấu trúc bao gồm chính hàm đó cộng với môi trường mà nó được xác định. Các cuộc gọi đến hàm đó sau đó được đánh giá bằng cách mở rộng môi trường ràng buộc trong đó hàm được xác định - không phải là môi trường mà nó được gọi.

Lisps cũ (bao gồm Emacs Lisp cho đến gần đây) có lambda, nhưng không phải là phạm vi từ vựng, vì vậy mặc dù bạn có thể tạo các hàm ẩn danh, cuộc gọi đến chúng sẽ được đánh giá trong môi trường gọi điện chứ không phải môi trường định nghĩa. đóng cửa. Tôi tin rằng Scheme là ngôn ngữ đầu tiên để có được quyền này. Sussman và Steele ban đầu của Lambda Papers về việc thực hiện Đề án làm cho đọc lớn tâm trí mở rộng cho bất cứ ai muốn hiểu phạm vi, trong số nhiều thứ khác.

Các vấn đề liên quan