Вот реализация с использованием изменяемых, Unboxed векторов вместо конструкций более высокого уровня. Он также использует conduit
для чтения файла, чтобы избежать ленивых операций ввода-вывода.
import Control.Monad.IO.Class
import qualified Data.ByteString as S
import Data.Conduit
import Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import qualified Data.Vector.Unboxed.Mutable as VM
import Data.Word (Word8)
type Freq = VM.IOVector Int
newFreq :: MonadIO m => m Freq
newFreq = liftIO $ VM.replicate 256 0
printFreq :: MonadIO m => Freq -> m()
printFreq freq =
liftIO $ mapM_ go [0..255]
where
go i = do
x <- VM.read freq i
putStrLn $ show i ++ ": " ++ show x
addFreqWord8 :: MonadIO m => Freq -> Word8 -> m()
addFreqWord8 f w = liftIO $ do
let index = fromIntegral w
oldCount <- VM.read f index
VM.write f index (oldCount + 1)
addFreqBS :: MonadIO m => Freq -> S.ByteString -> m()
addFreqBS f bs =
loop (S.length bs - 1)
where
loop (-1) = return()
loop i = do
addFreqWord8 f (S.index bs i)
loop (i - 1)
-- | The main entry point.
main :: IO()
main = do
freq <- newFreq
runResourceT
$ sourceFile "random"
$$ CL.mapM_ (addFreqBS freq)
printFreq freq
Я побежал это на 500MB случайных данных и по сравнению с @ josejuan-х UArray основе ответа:
- трубные основе/изменяемые векторов: 1.006s
- UArray: 17.962s
Я думаю, что это должно быть возможно сохранить большую часть элегантности подхода высокого уровня josejuan пока держать скорость изменяемой реализации вектора, но у меня не был возможность попробовать реализовать что-то подобное еще , Также обратите внимание, что с некоторыми вспомогательными функциями общего назначения (такими как Data.ByteString.mapM или Data.Conduit.Binary.mapM) реализация может быть значительно проще, не влияя на производительность.
Вы также можете использовать play with this implementation on FP Haskell Center.
EDIT: Я добавил одну из этих недостающих функций в conduit
и немного очистил код; теперь это выглядит так:
import Control.Monad.Trans.Class (lift)
import Data.ByteString (ByteString)
import Data.Conduit (Consumer, ($$))
import qualified Data.Conduit.Binary as CB
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector.Unboxed.Mutable as VM
import System.IO (stdin)
freqSink :: Consumer ByteString IO (V.Vector Int)
freqSink = do
freq <- lift $ VM.replicate 256 0
CB.mapM_ $ \w -> do
let index = fromIntegral w
oldCount <- VM.read freq index
VM.write freq index (oldCount + 1)
lift $ V.freeze freq
main :: IO()
main = (CB.sourceHandle stdin $$ freqSink) >>= print
Единственная разница в функциональности - это то, как печатается частота.
Что произойдет, если вы скомпилируете с помощью 'ghc -O2'? Оптимизации строгости, которые могли бы избежать проблемы с памятью, могут тогда только ударить. –
Все еще выходит из памяти. –
Что делать, если вы переключитесь на Data.Map.Strict: http://hackage.haskell.org/package/containers-0.5.0.0/docs/Data-Map-Strict.html –