2013-09-24 28 views
2

我聽說在Haskell中,我們可以使用MonadFix來訪問將在未來評估的值。但我認爲Monad只是語法糖,所以應該有類似的東西,可以在純函數中實現。所以,我試過如下:如何使「時間機器」在Haskell中工作?

timemachine :: [a] -> (a -> Int -> b -> b) -> b -> b 
timemachine al f b = result where 
    ~(total, result) = foldr app (0,b) al 
    app a (i,b1) = (i+1, f a (total - i) b1) 

main :: IO() 
main = print $ timemachine "ddfdfeef" (\x i y -> (x,i):y) [] 

但預計不會輸出:

[('d',1),('d',2),('f',3),('d',4),('f',5),('e',6),('e',7),('f',8)] 

理想的結果應該是

[('d',8),('d',7),('f',6),('d',5),('f',4),('e',3),('e',2),('f',1)] 

難道我做錯了什麼?

+0

你爲什麼期待這樣的結果?懶惰(?)來自你的代碼? –

+0

那麼我的實現可能完全錯誤,但有沒有什麼正確的方法來獲得預期的結果?關鍵是我需要訪問未來的價值 - 在上面的例子中,列表的長度。 –

+0

如果我正確理解你,你需要使用'MonadFix'來請求一些代碼的解除版本。你給了我們最初的東西似乎是公平的。 :-) –

回答

9

跳過大約MonadFix的部分,似乎要使用一個值,該值至少部分地需要本身要構造(total是基於這又是基於total摺疊)。

你實際上已經這麼做了,只有你的期望不符合你的摺疊!要查看你可以改變timemachine

timemachine al f b = result where 
    ~(total, result) = foldr app (0,b) al 
    app a (i,b1) = (i+1, f a i b1) 

這將產生

[('d',7),('d',6),('f',5),('d',4),('f',3),('e',2),('e',1),('f',0)] 

所以total-i工作的問題,它只是你的積累i S中的不是你想要的。

現在,爲什麼?那麼,你使用foldr該工程以類似:

app 'd' (app 'd' (app 'f' ... (app 'f' (0,b))))))))) 

所以計數是app正在做的是被從列表右側的工作。如果我們不是將其更改爲foldl從對方的作品名單,並改變一些代碼的其餘部分的排隊吧:

timemachine :: [a] -> (a -> Int -> b -> b) -> b -> b 
timemachine al f b = result where 
    ~(total, result) = foldl app (0,b) al 
    app (i,b1) a = (i+1, f a (total-i) b1) 

main :: IO() 
main = print $ timemachine "ddfdfeef" (\x i y -> y++[(x,i)]) [] 

你得到你想要的東西:

[('d',8),('d',7),('f',6),('d',5),('f',4),('e',3),('e',2),('f',1)] 
+0

很好的答案,你解決了這個問題! –

+0

我應該用'foldl''來代替嗎?我聽說'foldl'是要避免的。 –

+1

通常使用'foldl''比'foldl'更受歡迎,因爲它的累加器是嚴格的,當你向左摺疊時,你通常對懶惰不感興趣。然而,在這種情況下,有兩個問題: 1.我們的累加器是一對,'seq'(由'fold'使用)只評估HNF,'app'已經這樣做,所以沒有嚴格性增益那裏。 2.我們對懶惰感興趣,這就是爲什麼這項工作。如果我們用'deepseq'這樣的東西來摺疊,它會盡早評估結果列表,而且它不會完成。 – ollanta