2017-10-05 22 views

Trả lời

6

Trong Erlang các nhà điều hành = là cả phânkhẳ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/2thự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 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 ...)

5

Trong trường hợp đầu tiên bạn đã bị ràng buộc Tổng số đến 15. Trong Erlang, biến được unmutable, nhưng trong vỏ khi bạn viết Total = 15. bạn không thực sự tạo biến Total, trình bao sẽ làm tốt nhất để bắt chước hành vi bạn sẽ có nếu bạn đang chạy một ứng dụng, và nó lưu trữ trong một bảng các cặp vợ chồng {"Total",15}.

Trên dòng tiếp theo, bạn xác định niềm vui Tính toán. trình phân tích cú pháp tìm biểu thức Total=2*Number và nó đi qua bảng của nó để phát hiện rằng Tổng số đã được xác định trước đó. Việc đánh giá được chuyển thành một cái gì đó tương đương với 15 = 2*Number.

Vì vậy, trong dòng thứ ba, khi bạn hỏi để đánh giá Calculate(6), nó đi để tính toán và đánh giá 15 = 2*6 và phát hành các thông báo lỗi

exception error: no match of right hand side value 12

Trong ví dụ thứ hai, Tổng chưa được định nghĩa khi bạn xác định chức năng. Chức năng được lưu trữ mà không được gán (Tổng số không được sử dụng nữa), ít nhất là không gán cho một biến toàn cầu. Vì vậy, không có xung đột khi bạn xác định Tổng số và không có lỗi khi bạn đánh giá Calculate(6).

Hành vi sẽ giống hệt nhau trong mô-đun được biên dịch.

+1

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 –

+0

@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

1

Biến 'Tổng' đã được gán giá trị 15, vì vậy bạn KHÔNG thể sử dụng cùng tên biến Tổng số trong dòng thứ hai. Bạn nên thay đổi sang tên khác Total1 hoặc Total2 ...

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