2016-03-07 63 views
-2

我正在學習haskell幾天,懶惰就像流行語一樣。因爲我不熟悉懶惰(我一直主要使用非功能性語言),所以對我來說這並不容易。懶惰的本質。 Haskell

所以,我要求任何excerise /例子,告訴我什麼是懶惰的事實。

在此先感謝;)

+0

取一個從0到* infinity *的數字列表,將一個函數映射到它上面(迭代它,比方說所有的值乘以2),然後打印出前10個數字。 - 懶惰,你可以完全按照描述來完成這項任務;沒有它,你必須以不同的方式思考和編碼。 – deceze

+3

從您的教程中引用您遇到問題的內容:定義,聲明,代碼片段 - 無論提及的是懶惰。解釋你具體的困難是什麼。 –

回答

3

這裏有一些有趣的東西:動態編程,每個介紹的麻煩。算法學生,變得簡單自然當寫在一個懶惰和功能的語言。以the example of string edit distance。這是測量兩個DNA鏈相似程度或兩個二進制可執行文件的兩個版本之間有多少字節發生變化的問題,或者是兩個字符串有多'不同'。動態規劃算法,數學表達,很簡單:

let: 

• d_{i,j} be the edit distance of 
    the first string at index i, which has length m 
    and the second string at index j, which has length m 
• let a_i be the i^th character of the first string 
• let b_j be the j^th character of the second string 

define: 

d_{i,0} = i     (0 <= i <= m) 
d_{0,j} = j     (0 <= j <= n) 
d_{i,j} = d_{i - 1, j - 1} if a_i == b_j 
d_{i,j} = min {    if a_i != b_j 
    d_{i - 1, j} + 1 (delete) 
    d_{i, j - 1} + 1 (insert) 
    d_{i - 1, j - 1} + 1 (modify) 
} 

return d_{m, n} 

而且算法,在Haskell表示,如下的算法相同的形狀:

distance a b = d m n 
    where (m, n) = (length a, length b) 
     a'  = Array.listArray (1, m) a 
     b'  = Array.listArray (1, n) b 

     d i 0 = i 
     d 0 j = j 
     d i j 
      | a' ! i == b' ! j = ds ! (i - 1, j - 1) 
      | otherwise = minimum [ ds ! (i - 1, j)  + 1 
           , ds ! (i, j - 1)  + 1 
           , ds ! (i - 1, j - 1) + 1 
           ] 

     ds = Array.listArray bounds 
       [d i j | (i, j) <- Array.range bounds] 
     bounds = ((0, 0), (m, n)) 

在嚴格的語言,我們止跌」不能直接定義它,因爲數組的單元格將被嚴格評估。在Haskell中,我們可以將每個單元格的定義引用其他單元格的定義,因爲Haskell是惰性的 - 只有在d m n向陣列請求最後一個單元格的值時纔會評估定義。一種懶惰的語言讓我們設置了一個站立的多米諾骨牌圖;只有當我們要求價值時,我們才需要計算這個價值,這個價值推翻了第一個多米諾骨牌,這個多米諾骨牌推倒了所有其他多米諾骨牌。 (用嚴格的語言,我們必須設置一系列閉包,做Haskell編譯器爲我們自動完成的工作,很容易在嚴格語言和惰性語言之間轉換實現;這是所有語言都表達哪種想法的問題更好。)

blog post做了一個更好的解釋所有這一切。

4

在Haskell中,您可以創建一個無限列表。例如,所有自然數:

[1,2..] 

如果Haskell一次加載內存中的所有項目,這是不可能的。要做到這一點,你需要無限的記憶。

懶惰可讓您根據需要獲取數字。

2

所以,我要求任何excerise /例子,告訴我什麼是懶惰的事實。

點擊懶惰haskell.org得到規範的例子。還有很多其他的例子可以說明延遲評估的概念,它不受程序邏輯某些部分的影響。懶惰肯定不慢,但與大多數命令式編程語言通用的熱切評估正好相反。

2

懶惰是非嚴格功能評估的結果。考慮1秒的「無限」的文章:

ones = 1:ones 

在定義的時候,(:)功能不評估;如果有必要,ones只是一個承諾。這樣的時間會當你的模式匹配:

myHead :: [a] -> a 
myHead (x:rest) = x 

myHead ones被調用,xrest需要,而是針對1:ones模式匹配簡單地結合x 1和restones;我們目前不需要評估ones,所以我們不需要。

使用..「運算符」作爲算術序列的無限列表的語法是調用enumFromenumFromThen時的糖。也就是說

-- An infintite list of ones 
ones = [1,1..] -- enumFromThen 1 1 

-- The natural numbers 
nats = [1..] -- enumFrom 1 

如此反覆,懶惰只是來源於enumFrom非嚴格的評估。

+0

「懶惰是非嚴格功能評估的結果」並不完全正確。在你的例子中,'ones'不是一個函數,它是一個(無限)列表。而在一種渴望的語言(例如OCaml)中,它確實會導致無限循環。但是我們可以利用這樣的事實,即在lambda表達式下不執行評估以使用例如相似的惰性列表對模型進行建模。 'type'a stream ='a *(unit - >'a)'和defininig'let rec ones()= Cons(1,ones)'的缺點。 – gallais

+0

由於'(:)'不嚴格,它實際上是一個有限的列表,其中有1個,而對'ones'的調用沒有被評估。當你需要用尾巴或尾巴等東西來完全評估尾巴時,清單的「無限」性質纔會變得明顯。 – chepner

1

與其他語言不同,Haskell分離了對象的創建和定義....您可以使用Debug.Trace輕鬆地觀看此操作。

你可以這樣定義

aValue = 100 

一個變量(在右邊的值可能包括一個複雜的評估,但讓我們保持簡單)

要看看這個代碼曾經被稱爲,你可以用表達的Debug.Trace.trace這樣

import Debug.Trace 

aValue = trace "evaluating aValue" 100 

請注意,這不會改變的aValue定義,它只是強制程序在運行時實際創建該表達式時輸出「評估值」。

(另請注意,trace被認爲是不安全的生產代碼,應只用於調試)。

現在,嘗試了兩個實驗....寫兩個不同main小號

main = putStrLn $ "The value of aValue is " ++ show aValue 

main = putStrLn "'sup" 

運行時,你將看到的第一個程序實際上創建了安勤(你會看到「創建一個值」的消息,而第二個沒有。

這是懶惰的想法....你可以在程序中放置儘可能多的定義,只要你想那些使用的將在運行時實際創建。

實際使用這個可以看到無限大小的物體。許多列表,樹等都有無數的元素。你的程序只會使用有限數量的值,但你不想用這個混亂的事實來混淆對象的定義。就拿在這裏其他的答案中給出的無限列表....

[1..] -- = [1,2,3,4,....] 

你可以在這裏使用跟蹤再次看到懶惰的行爲,雖然你將不得不寫出來的一個變種[1 ..]在擴展形式來做到這一點。

f::Int->[Int] 
f x = trace ("creating " ++ show x) (x:f (x+1)) --remember, the trace part doesn't change the expression, it is just used for debugging 

現在您將看到只有您使用的元素被創建。

main = putStrLn $ "the list is " ++ show (take 4 $ f 1) 

產生 創建1 創建2 創建3 創建4 列表被[1,2,3,4]

main = putStrLn "yo" 

不會顯示任何項被創建。