[英]How does this haskell code work?
我是一名新生,我正在攻讀計算機科學專業。 我們正在處理Haskell,雖然我理解Haskell的想法,但我似乎無法弄清楚我們應該看到的代碼片段究竟是如何工作的:
module U1 where
double x = x + x
doubles (d:ds) = (double d):(doubles ds)
ds = doubles [1..]
我承認,對於知道發生了什么事的人來說似乎相當簡單,但我無法繞過它。 如果我寫“take 5 ds”,它顯然會回復[2,4,6,8,10]。 我沒有得到的,是為什么。
這是我的思路:我打電話給ds,然后尋找雙打。 因為我也提交了值[1 ..],雙精度(d:ds)應該表示d = 1且ds = [2 ..],對嗎? 然后我將d加倍,返回2並將其放在列表的開頭(數組?)。 然后它調用自身,將ds = [2 ..]轉換為d = 2和ds = [3 ..],然后再次將d加倍並再次調用自身,依此類推,直到它可以返回5個值, [2,4,6,8,10]。
首先,我的理解是對的嗎? 我的思緒中是否有任何嚴重的錯誤? 第二,因為它似乎將所有加倍的d保存到列表中以便稍后調用,那么該列表的名稱是什么? 我在哪里完全定義它?
在此先感謝,希望你能幫助學生理解這個x)
我認為關於doubles
如何遍歷無限列表的每個元素的遞歸/循環部分你是正確的。
現在關於
它似乎將所有加倍的d保存到列表中以便稍后調用,該列表的名稱是什么? 我在哪里完全定義它?
這涉及在Haskell中稱為Lazy Evaluation的功能。 該列表未預先計算並存儲在任何位置。 相反,您可以想象列表是C ++中的一個函數對象,可以在需要時生成元素。 (您可能會看到的正常語言是表達式是按需評估的)。 所以,當你這樣做
take 5 [1..]
[1..]
可以被視為一個函數對象,當與head
, take
等一起使用時會生成數字。所以,
take 5 [1..] == (1 : take 4 [2..])
這里[2..]
也是一個為你提供數字的“函數對象”。 同樣,你可以擁有
take 5 [1..] == (1 : 2 : take 3 [3..]) == ... (1 : 2 : 3 : 4 : 5 : take 0 [6..])
現在,我們不需要關心[6..]
,因為對於任何xs
take 0 xs
xs
是[]
。 因此,我們可以擁有
take 5 [1..] == (1 : 2 : 3 : 4 : 5 : [])
無需存儲任何“無限”列表,如[2..]
。 如果您想了解Lazy計算實際上是如何發生的,可以將它們視為函數對象/生成器。
你的思路看起來是正確的。 其中唯一的微小不准確在於使用表達式來描述計算,例如“它加倍2 然后調用自身......”。 在純函數式編程語言中,例如Haskell,實際上沒有固定的評估順序。 具體來說,在
double 1 : double [2..]
在將列表的其余部分加倍之前是否發生加倍1是未指定的。 理論結果保證了秩序確實無關緊要,因為 - 大致 - 即使你以不同的順序評估你的表達式,你也會得到相同的結果。 我建議您使用Lambda Bubble Pop網站查看此屬性:您可以按不同順序彈出氣泡以模擬任何評估順序。 無論你做什么,你都會得到相同的結果。
請注意,由於評估順序無關緊要,Haskell編譯器可以自由選擇它認為最適合您的代碼的任何評估順序。 例如,讓ds
定義為代碼的最后一行,並考慮
take 5 (drop 5 ds)
這導致[12,14,16,18,20]
。 請注意,編譯器不需要將前5個數字加倍,因為您正在刪除它們,因此可以在完全計算之前刪除它們(!!)。
如果你想進行實驗,可以自己定義一個計算成本非常高的函數(比如,在遞歸定義后寫下fibonacci
)。
fibonacci 0 = 0
fibonacci 1 = 1
fibonacci n = fibonacci (n-1) + fibonacci (n-2)
然后,定義
const5 n = 5
並計算
fibonacci 100
並觀察實際需要多長時間。 然后,評估
const5 (fibonacci 100)
並且看到結果立即到達 - 參數甚至沒有計算(!),因為不需要它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.