2010-09-13 72 views
2

我有一個文件,它看起來像這樣index : label指數的價值包含在0... 100000000標籤的範圍鍵分割110Mo文件可以是任何String價值,我想分裂這個文件,該文件每片100片中有110片每片有一片計算。我怎樣才能做到這一點?如何哈斯克爾

123 : "acgbdv" 

127 : "ytehdh" 

129 : "yhdhgdt" 

... 

9898657 : "bdggdggd" 
+3

什麼是「Mo」? – jrockway 2010-09-13 18:43:01

+0

萬對象? – kennytm 2010-09-13 18:48:10

+2

這是法國人的「MB」 - 巨型八位字節。 – Chuck 2010-09-13 23:19:41

回答

3

如果您使用字符串IO,你可以做到以下幾點:

import System.IO 
import Control.Monad 

-- | Process 100 lines 
process100 :: [String] -> MyData 
-- whatever this function does 

loop :: [String] -> [MyData] 
loop lns = go [] lns 
    where 
    go acc [] = reverse acc 
    go acc lns = let (this, next) = splitAt 100 lns in go (process100 this:acc) next 

processFile :: FilePath -> IO [MyData] 
processFile f = withFile f ReadMode (fmap (loop . lines) . hGetContents) 

注意,這個函數會默默地處理最後一塊,即使它不完全是100線。

類似字符串和文本的包通常提供像lineshGetContents這樣的函數,所以您應該可以輕鬆地將此函數適用於它們中的任何一個。

重要的是要知道你正在處理每個片的結果,因爲你不想讓這些數據超過必要的時間。理想情況下,在計算完每個切片後,數據將被完全消耗並且可能被gc'd。通常要麼單獨的結果被合併成一個單一的數據結構(一個「摺疊」),要麼每個單獨處理(可能會輸出一行到一個文件或類似的東西)。如果它是一個折,你應該改變「循環」,看起來像這樣:

loopFold :: [String] -> MyData -- assuming there is a Monoid instance for MyData 
loopFold lns = go mzero lns 
    where 
    go !acc [] = acc 
    go !acc lns = let (this, next) = splitAt 100 lns in go (process100 this `mappend` acc) next 

loopFold功能使用爆炸方式迫使「邁德特」的評價(使用「語言BangPatterns」編譯啓用)。根據MyData的不同,您可能需要使用deepseq以確保其完全評估。

相反,如果你在寫每一行輸出,離開loop,因爲它是和更改processFile

processFileMapping :: FilePath -> IO() 
processFileMapping f = withFile f ReadMode pf 
    where 
    pf = mapM_ (putStrLn . show) <=< fmap (loop . lines) . hGetContents 

如果你有興趣枚舉/ iteratee風格的處理,這是一個非常簡單的問題。我不能不知道process100正在做什麼類型的工作,但它會涉及enumLinestake

是否有必要一次處理完整的100行,還是隻是爲了提高效率而分段處理?如果是後者,不要擔心。使用實際的摺疊函數或類似於processFileMapping的函數,一次最好處理一行。