2011-06-15 111 views
8

我學習Haskell和我下面的表達式上Haskell Wiki 真的困擾了我:自我參照的功能

fibs = 0 : 1 : zipWith (+) fibs (tail fibs) 

我不能完全弄清楚爲什麼這個工程。

如果我應用標準柯里格邏輯(zipWith (+))返回一個函數將列表作爲參數,然後返回另一個函數,它將另一個列表作爲參數返回一個列表(zipWith::(a -> b -> c) -> [a] -> [b] -> [c])。因此,fibs是對列表的引用(尚未評估),(tail fibs)是相同(未評估)列表的尾部。當我們嘗試評估(take 10 fibs)時,前兩個元素綁定到01。換句話說,fibs==[0,1,?,?...](tail fibs)==[1,?,?,?]。第一次添加完成後fibs變爲[0,1,0+1,?,..]。同樣,在第二次加入後,我們得到[0,1,0+1,1+(0+1),?,?..]

  • 我的邏輯是否正確?
  • 有沒有一個更簡單的方式來解釋這一點? (來自知道Haskell編譯器對此代碼做什麼的人的任何見解?)(鏈接和引用是受歡迎的)
  • 確實,這種類型的代碼僅適用於懶惰評估?
  • 我做什麼評估fibs !! 4
  • 此代碼是否假定zipWith首先處理元素? (我想應該沒有,但我不明白爲什麼不)

EDIT2:我只是發現了上述問題,並問及回答here。如果我浪費了任何人的時間,我很抱歉。

編輯:這是一個比較困難的情況下,理解(來源:Project Euler forums):

filterAbort :: (a -> Bool) -> [a] -> [a] 
filterAbort p (x:xs) = if p x then x : filterAbort p xs else [] 

main :: Int 
main = primelist !! 10000 
     where primelist = 2 : 3 : 5 : [ x | x <- [7..], odd x, all (\y -> x `mod` y /= 0) (filterAbort (<= (ceiling (sqrt (fromIntegral x)))) primelist) ] 

注意all (\y -> x mod y /= 0)...怎麼能指X這裏不是導致無限遞歸?

+0

這似乎是正確的。對我而言,這似乎也很簡單 - 當你更加和Haskell一起工作時,你的大腦會開始採用這些模式,這對你也很簡單。好的開始。 – luqui 2011-06-16 05:38:56

+2

首先,'filterAbort'和'takeWhile'是一樣的。其次,你可以通過編寫'[7,9 ..]'來避免偶數。第三,如果你使用'[5,7 ..]',那麼你不需要在你的初始列表中有'5'。最後,這個原理的原因是相當深的。這是因爲對於每個素數「p」,在「p^2」之前還有一個素數。是Lindemann定理的一個微不足道的結果(p和2p之間的素數)。 – augustss 2011-06-16 08:09:52

+0

謝謝,8月。優化和清理你建議是有道理的。至於終止,你能否詳細說明一下?在'(\ y - > x mod y/= 0)'中綁定了'x'?我懷疑我的錯誤在於認爲它是無限的列表。如果它只有一個值(比如'7'),那麼就沒有問題了。你可否確認? – 2011-06-16 11:14:21

回答

15

我會跟蹤評價你:

> fibs = 0 : 1 : zipWith (+) fibs (tail fibs) 

有了:

> zipWith f (a:as) (b:bs) = f a b : zipWith f as bs 
> zipWith _ _  _  = [] 

> tail (_:xs)    = xs 
> tail []     = error "tail" 

所以:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs) 
↪ fibs = 0 : 1 : ((+) 0 1 : zipWith (+) (tail fibs) (tail (tail fibs))) 
↪ fibs = 0 : 1 : 1 : ((+) 1 1 : zipWith (+) (tail (tail fibs)) (taii (tail (tail fibs)))))  
↪ fibs = 0 : 1 : 1 : 2 : ((+) 1 2 : zipWith (+) (tail (tail (tail fibs))) (tail (taii (tail (tail fibs)))))) 
↪ fibs = 0 : 1 : 1 : 2 : 3 : ((+) 2 3 : zipWith (+) (tail (tail (tail (tail fibs)))) (tail (tail (taii (tail (tail fibs))))))) 

等,所以,如果你索引到這種結構,它會強制評估每一步纖維,直到找到指標。

爲什麼這樣高效?一個字:分享

由於計算fibs,它在堆中增長,記錄已經計算機的值。之後對fibs的返回引用可重新使用fibs的所有先前計算的值。免費記憶!

分享在堆中看起來像什麼?

enter image description here

至於你問的列表的末尾對象,哈斯克爾計算未來值,記錄它,並移動,自我參照下一個節點。

這終止的事實嚴重依賴於懶惰。

+0

謝謝,唐。對於Haskell如何知道爲了獲得下一個需要調用fibs的元素,我還有點模糊,如果fibs實際上是一個列表,那麼「調用fibs」意味着什麼。 我四處張望,我發現[類似的問題](http://stackoverflow.com/questions/3887201/funky-haskell-lazy-list-implicit-recursion),但我仍然不確定有什麼區別'1'節點和'fibs ...'節點連接到Haskell編譯器。 – 2011-06-16 01:09:47

+1

你可以把'fibs'看作只是一個指針。所以每次你提到'fibs'時,都會查找它的值。該值恰好是一個列表。 – 2011-06-16 01:16:53

+0

啊!那是個很好的觀點。我應該糾正我的問題。什麼是符號'0:1:zipWith ...'產生?它開始作爲一個列表,我明白。但它然後到達列表中的一個節點,這是一個函數調用?或者最好將列表中的所有節點視爲函數調用?即說'ident x = x',所以'fib =(ident 0):(ident 1):(zipWith ...)'。我這種情況下編譯器評估*所有*單元格,但最後一個最終作爲函數調用返回一個列表?並且'(zipWith ...)恰好指向'fibs'列表。對? – 2011-06-16 01:21:49