Trong Erlang các nhà điều hành =
là cả phân và khẳng định.
Nếu tôi làm điều này:
A = 1,
A = 2,
chương trình của tôi sẽ sụp đổ. Tôi chỉ nói với nó rằng A = 1
trong đó, khi A
là không liên kết (chưa tồn tại dưới dạng nhãn), bây giờ nó được gán giá trị 1 mãi mãi và mãi mãi - cho đến khi phạm vi thực hiện thay đổi. Vì vậy, sau đó khi tôi nói với nó rằng A = 2
nó cố gắng để khẳng định rằng giá trị của A
là 2, mà nó không phải là. Vì vậy, chúng tôi gặp sự cố khi kết hợp không tốt.
Scope trong Erlang được xác định bởi hai yếu tố:
- Định nghĩa của hàm hiện tại. Phạm vi này là tuyệt đối trong khoảng thời gian định nghĩa hàm.
- Định nghĩa lambda hiện tại hoặc danh sách hiểu. Phạm vi này là địa phương đối với lambda nhưng cũng đóng trên bất kỳ giá trị nào từ phạm vi bên ngoài được tham chiếu.
Những phạm vi là luôn superceded tại thời điểm họ được khai báo bởi bất cứ điều gì là trong phạm vi bên ngoài. Đó là cách chúng tôi đóng cửa bằng các chức năng ẩn danh. Ví dụ, giả sử tôi đã có một ổ cắm Tôi muốn gửi một danh sách dữ liệu thông qua. Ổ cắm đã được gắn với tên biến Socket
trong phần đầu của hàm, và chúng tôi muốn sử dụng một thao tác danh sách để ánh xạ danh sách các giá trị để gửi đến một hiệu ứng phụ được gửi qua ổ cắm cụ thể đó. Tôi có thể đóng cửa trên giá trị của các ổ cắm trong cơ thể của một lambda, mà có tác dụng tách lạng bộ giá trị mà ra khỏi hoạt động tổng quát hơn "gửi một số dữ liệu":
send_stuff(Socket, ListOfMessages) ->
Send = fun(Message) -> ok = gen_tcp:send(Socket, Message) end,
lists:foreach(Send, ListOfMessages).
Mỗi lần lặp của hoạt động danh sách lists:foreach/2
chỉ có thể chấp nhận hàm số của arity 1 làm đối số đầu tiên. Chúng tôi đã tạo ra một đóng cửa mà bắt giữ giá trị của Socket
nội bộ đã (vì đó đã được ràng buộc trong phạm vi bên ngoài) và kết hợp nó với unbound, biến bên trong Message
. Cũng lưu ý rằng chúng tôi đang kiểm tra xem gen_tcp:send/2
có hoạt động mỗi lần trong lambda hay không bằng cách xác nhận rằng giá trị trả về của gen_tcp:send/2
là thực sựok
.
Đây là tài sản vô cùng hữu ích .
Vì vậy, với ý nghĩ đó, chúng ta hãy nhìn vào mã của bạn:
1> Total = 15.
2> Calculate = fun(Number)-> Total = 2 * Number end.
3> Calculate(6).
Trong đoạn mã trên bạn vừa gán một giá trị để Total
, nghĩa là bạn đã tạo ra một nhãn cho giá trị đó (giống như chúng ta đã giao Socket
trong ví dụ trên). Sau đó bạn đang khẳng định rằng giá trị của Total
là bất cứ điều gì là kết quả của 2 * Number
có thể - mà không bao giờ có thể là sự thật kể từ Total
là một số nguyên nên 2 * 7.5
sẽ không cắt nó cả, bởi vì kết quả sẽ là 15.0
, không 15
.
1> Calculate = fun(Number)-> Total = 2 * Number end.
2> Total = 15.
3> Calculate(6).
Trong ví dụ này, tuy nhiên, bạn đã có một biến nội gọi Total
mà không đóng đối với bất kỳ giá trị khai báo trong phạm vi bên ngoài. Sau đó, bạn là tuyên bố nhãn ở phạm vi bên ngoài được gọi là Total
, nhưng vào thời điểm này định nghĩa lambda trên dòng đầu tiên đã được chuyển thành hàm trừu tượng và nhãn Total
như đã sử dụng ở đó đã được đưa hoàn toàn vào không gian bất biến của định nghĩa hàm mới, gán cho Calculate
được biểu diễn. Vì vậy, không có xung đột.
Hãy xem xét những gì xảy ra, ví dụ, với cố gắng tham chiếu giá trị nội tại từ một danh sách hiểu:
1> A = 2.
2
2> [A * B || B <- lists:seq(1,3)].
[2,4,6]
3> A.
2
4> B.
* 1: variable 'B' is unbound
Đây không phải là những gì bạn mong chờ từ, nói, Python 2:
>>> a = 2
>>> a
2
>>> [a * b for b in range(1,4)]
[2, 4, 6]
>>> b
3
Ngẫu nhiên, điều này đã được sửa trong Python 3:
>>> a = 2
>>> a
2
>>> [a * b for b in range(1,4)]
[2, 4, 6]
>>> b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
(Và tôi sẽ cung cấp một JavaScrip t ví dụ để so sánh là tốt, nhưng các quy tắc phạm vi có quá hoàn toàn điên rồ, thậm chí không quan trọng ...)
Tôi đoán cần thêm một số thông tin về phạm vi (ngữ cảnh) vì nó làm cho câu trả lời rõ ràng hơn. "Bảng tìm kiếm" không phải lúc nào cũng dễ hiểu. Một số liên kết: http://learnyousomeerlang.com/higher-order-functions, http://www.erlang.org/course/advanced#scope, http://icai.ektf.hu/pdf/ICAI2007-vol2-pp137 -145.pdf –
@Atomic_alarm: Tôi sợ bạn đúng: o) Trong thực tế tôi đề cập đến bảng này (một từ điển quy trình, trong một quá trình làm việc song song với vỏ) vì sự khác biệt nhỏ giữa hành vi trình bao và mã mô-đun luôn luôn làm tôi băn khoăn, đặc biệt là thực tế là có thể "quên" một biến trong shell: f (Foo). Nhưng nó là chính xác, nó không giúp để hiểu câu trả lời của tôi, tôi thậm chí còn cần phải đề cập rằng nó hoạt động giống nhau trong một mô-đun ... – Pascal