2013-01-15 106 views
3

我在想Haskell中的列表,並且我認爲在其他語言中,一個不使用列表的一切。當然,如果你以後需要這些值,你可能想要存儲一個列表,但是如果它只是一次性的,比如從[1..n]開始迭代,爲什麼使用一個列表,其中真正需要的變量是一個增加的變量?管道,替換列表?

我還閱讀了關於「列表融合」的內容,並指出儘管Haskell編譯器試圖實現這種優化以消除中間列表,但它們往往不成功,導致垃圾收集器不得不清理僅被使用一次的列表。

另外,如果你不小心一個可以輕鬆地共享一個列表,這意味着垃圾收集不清理,這可能會導致與以前的設計在不斷運行的算法運行內存空間。

所以我認爲這將是最好的,以避免完全名單,至少在一個實際上並不想「商店」名單。

我然後在conduit來了,它是說:

解決數據流的問題,允許生產, 改造,並在不斷的 存儲器中的數據流的消費。

這聽起來很不錯。我知道conduit是專爲IO資源獲取和發佈問題的問題而設計的,但是可以將它用作替代列表的替代品嗎?

例如,我可以做到以下幾點:

fold f3 $ take 10 $ map f2 $ unfold f1 init_value 

而且有一些適當放置類型標註,使用管道的整個過程,而不是名單?

我希望或許classy-prelude會允許這樣的代碼,但我不知道。如果有可能,有人可以舉一個例子,像上面這樣說嗎?

+1

你對名單的直覺是不正確的,我想,你也可以忘記了懶惰的評價,當你談論「存儲」一名單。在任何情況下,「管道」正在解決的問題都是懶惰IO提出的問題。也看看'vector'軟件包。 – jberryman

回答

8

列表計算在不斷的內存流相同的情況下,他們會爲conduit。中間數據結構的存在或不存在並不影響它是否在恆定內存中運行。它所改變的只是它所居住的恆定記憶的效率和大小。

不要期望導管運行內存少於等效列表計算。它實際上應該佔用更多內存,因爲導管步驟比列表單元有更大的開銷。另外,導管目前沒有流融合。前段時間有人did experiment with that,雖然沒有納入圖書館。另一方面,列表在很多情況下可以並且確實會融合以去除中間數據結構。

要記住的重要一點是流式傳輸並不一定意味着毀林(即去除中間數據結構)。

+0

因此導管不夠智能,無法將'map f $ map g'結合到'map(f。g)'中? – Clinton

+1

@Clinton原則上你可以添加一個重寫規則,將左側轉換爲右側,但是我爲自己的'pipes'庫嘗試了類似的東西,並且它不能可靠地觸發。然而,我鏈接的流式融合版本實際上會自動優化左側到右側,而不會有任何提示,但是這帶來了它自己的問題。問題的核心在於'ghc'並不是真正擅長收緊循環,這就是爲什麼人們首先想到流融合,以便將遞歸代碼轉換爲非遞歸代碼。 –

4

conduit肯定是不適合這種用例的,但它在理論上可以使用這種方式。我親自爲markdown包做了這樣的工作,在這裏比額外的conduit管道更方便,而不是直接處理清單。

如果你把它和classy-prelude-conduit放在一起,你可以得到一些相對簡單的代碼。我們當然可以增加更多出口到classy-prelude-conduit以更好地優化這個用例。現在,這裏是下面的你在上面佈置什麼的基本要點一個例子:

{-# LANGUAGE NoImplicitPrelude #-} 
{-# LANGUAGE OverloadedStrings #-} 
import ClassyPrelude.Conduit 
import Data.Conduit.List (unfold, isolate) 
import Data.Functor.Identity (runIdentity) 

main = putStrLn 
    $ runIdentity 
    $ unfold f1 init_value 
    $$ map f2 
    =$ isolate 10 
    =$ fold f3 "" 

f1 :: (Int, Int) -> Maybe (Int, (Int, Int)) 
f1 (x, y) = Just (x, (y, x + y)) 

init_value = (1, 1) 

f2 :: Int -> Text 
f2 = show 

f3 :: Text -> Text -> Text 
f3 x y = x ++ y ++ "\n"