在Erlang shell裏爲什麼下面產生不同的結果?爲什麼輸出在shell中的這兩個erlang表達式序列中有所不同?
1> Total=15.
2> Calculate=fun(Number)-> Total=2*Number end.
3> Calculate(6).
異常錯誤:沒有匹配的右手側的值12
1> Calculate=fun(Number)-> Total=2*Number end.
2> Total=15.
3> Calculate(6).
在Erlang shell裏爲什麼下面產生不同的結果?爲什麼輸出在shell中的這兩個erlang表達式序列中有所不同?
1> Total=15.
2> Calculate=fun(Number)-> Total=2*Number end.
3> Calculate(6).
異常錯誤:沒有匹配的右手側的值12
1> Calculate=fun(Number)-> Total=2*Number end.
2> Total=15.
3> Calculate(6).
在二郎山=
運營商既分配和斷言。
如果我這樣做:
A = 1,
A = 2,
我的程序會崩潰。我剛剛告訴它,A = 1
哪個,當A
未被綁定(尚未作爲標籤存在)時,它現在被永久賦值1,直到執行範圍發生變化。那麼當我告訴它A = 2
它試圖斷言A
的值是2,事實並非如此。所以我們遇到了一場糟糕的比賽。
範圍由兩件事情定義。在函數定義期間,該範圍爲絕對值。
這些示波器是總是在它們被無論是在外部聲明時間所取代。這就是我們如何使用匿名函數進行閉包。例如,假設我有一個套接字,我想發送一個數據列表。套接字已經綁定到函數頭部的變量名稱Socket
,並且我們希望使用列表操作來映射要發送的值列表,以便通過特定套接字發送的副作用。
send_stuff(Socket, ListOfMessages) ->
Send = fun(Message) -> ok = gen_tcp:send(Socket, Message) end,
lists:foreach(Send, ListOfMessages).
列表操作的每次迭代:我可以一個lambda的身體,有譁衆取寵該值超出「發送一些數據」更普遍的操作效果內關閉在插座的價值lists:foreach/2
只能接受arity 1的函數作爲其第一個參數。我們已經創建了一個閉包,它已經在內部捕獲Socket
的值(因爲它已經在外部範圍內綁定),並將其與未綁定的內部變量Message
結合。還要注意的是,我們正在檢查gen_tcp:send/2
是否每次都在拉姆達內工作,因爲聲稱gen_tcp:send/2
的返回值爲確實是ok
。
這是一個超級有用的財產。
所以考慮到這一點,讓我們看看你的代碼:
1> Total = 15.
2> Calculate = fun(Number)-> Total = 2 * Number end.
3> Calculate(6).
在上面的代碼中你只是分配一個值Total
,你已經創建了一個標籤,該值(就像我們的意思在上例中已經分配了Socket
)。再後來你斷言說的Total
值是什麼的2 * Number
結果可能是 - 這絕不是真實的,因爲Total
是一個整數,所以2 * 7.5
不會削減它要麼,因爲結果將是15.0
,不15
。
1> Calculate = fun(Number)-> Total = 2 * Number end.
2> Total = 15.
3> Calculate(6).
在這個例子中,雖然你有叫Total
的內部變量不封蓋外範圍內聲明的任何值。後來,是在外部範圍中聲明瞭一個名爲Total
的標籤,但此時第一行上的lambda定義已被轉換爲抽象函數,並且標籤Total
已被完全賦予不可變的空間新功能定義的分配以Calculate
爲代表。因此,沒有衝突。
考慮發生了什麼,例如,與試圖從列表理解引用的內在價值:
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
這不是你們想的,說會發生什麼,巨蟒2:
>>> a = 2
>>> a
2
>>> [a * b for b in range(1,4)]
[2, 4, 6]
>>> b
3
順便說一句,這已經在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
(我會提供一個JavaScrip T的範例也是如此,但是那裏的範圍規則只是如此絕對瘋狂,它甚至不重要......)
的在第一種情況下,你已綁定到總15.二郎,變量被不變,但在shell中寫入時Total = 15.
您並不真正創建變量Total
,shell會盡其所能模仿您將擁有的行爲你正在運行一個應用程序,它將一對{"Total",15}
存儲在一個表中。
在下一行中,您定義了有趣的計算。解析器找到表達式Total=2*Number
,並通過它的表檢測Total是否被定義。評估變成了相當於15 = 2*Number
的東西。
因此,在第三行,當你問到評估Calculate(6)
,它關係到計算和評估15 = 2*6
,併發出錯誤信息
exception error: no match of right hand side value 12
在第二個例子中,總還沒有定義,當你定義功能。該函數沒有賦值而被存儲(總計不再使用),至少沒有賦值給全局變量。所以當你定義Total時沒有衝突,當你評估時沒有錯誤Calculate(6).
這個行爲在編譯模塊中是完全一樣的。
變量'Total'已經被賦值15,所以你不能使用相同的第二行中的變量名稱總數。你應該更改爲其他名稱Total1或Total2 ...
我想需要添加一些有關範圍(上下文)的信息,因爲它的答案更清晰。 「搜索表」並不總是容易理解。一些鏈接: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:恐怕你是對的:o)事實上,我提到這個表(一個進程字典,在一個與shell並行工作的進程中),因爲shell行爲和模塊代碼一直困擾着我,特別是可以「忘記」shell中的一個變量的事實:f(Foo)。但它是正確的,它無助於理解我的答案,我甚至覺得有必要提到它在模塊中的作用是一樣的...... – Pascal