我有一個文件,它看起來像這樣index : label
,指數的價值包含在0... 100000000
和標籤的範圍鍵分割110Mo文件可以是任何String
價值,我想分裂這個文件,該文件每片100片中有110片每片有一片計算。我怎樣才能做到這一點?如何哈斯克爾
123 : "acgbdv"
127 : "ytehdh"
129 : "yhdhgdt"
...
9898657 : "bdggdggd"
我有一個文件,它看起來像這樣index : label
,指數的價值包含在0... 100000000
和標籤的範圍鍵分割110Mo文件可以是任何String
價值,我想分裂這個文件,該文件每片100片中有110片每片有一片計算。我怎樣才能做到這一點?如何哈斯克爾
123 : "acgbdv"
127 : "ytehdh"
129 : "yhdhgdt"
...
9898657 : "bdggdggd"
如果您使用字符串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線。
類似字符串和文本的包通常提供像lines
和hGetContents
這樣的函數,所以您應該可以輕鬆地將此函數適用於它們中的任何一個。
重要的是要知道你正在處理每個片的結果,因爲你不想讓這些數據超過必要的時間。理想情況下,在計算完每個切片後,數據將被完全消耗並且可能被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
正在做什麼類型的工作,但它會涉及enumLines
和take
。
是否有必要一次處理完整的100行,還是隻是爲了提高效率而分段處理?如果是後者,不要擔心。使用實際的摺疊函數或類似於processFileMapping的函數,一次最好處理一行。
什麼是「Mo」? – jrockway 2010-09-13 18:43:01
萬對象? – kennytm 2010-09-13 18:48:10
這是法國人的「MB」 - 巨型八位字節。 – Chuck 2010-09-13 23:19:41