いくつかのオブジェクトを含む Haskell のリストがあります。そして、これらのオブジェクトのいずれかが特定の条件を満たすかどうかを調べる必要があります。そこで、次のように書きました。
any (\x -> check x) xs
しかし問題は、チェック操作が非常に高価であり、リストが非常に大きいことです。たとえば、実行時に現在の進行状況を確認したいのですが、50% (1000/2000 checked).
これを行うにはどうすればよいですか?
いくつかのオブジェクトを含む Haskell のリストがあります。そして、これらのオブジェクトのいずれかが特定の条件を満たすかどうかを調べる必要があります。そこで、次のように書きました。
any (\x -> check x) xs
しかし問題は、チェック操作が非常に高価であり、リストが非常に大きいことです。たとえば、実行時に現在の進行状況を確認したいのですが、50% (1000/2000 checked).
これを行うにはどうすればよいですか?
conduit
これを行う別の方法は、またはのようなストリーミング ライブラリを使用することpipes
です。これはpipesを使用したサンプルコードで、リストの要素が到着してチェックされるたびにドットを出力します:
import Pipes
import qualified Pipes.Prelude as P
bigList :: [Int]
bigList = [1,2,3,4]
check :: Int -> Bool
check = (>3)
main :: IO ()
main = do
result <- P.any check $ each bigList >-> P.chain (\_ -> putStrLn ".")
putStrLn . show $ result
(それぞれが Pipes モジュールの関数です。)
パーセンテージを表示したい場合P.chain (\_ -> putStrLn ".")
は、パイプラインの一部をもう少し賢くする必要があります。現在のパーセンテージを状態として保持し、リストの長さを知っている必要があります。(リストが膨大で遅延生成されている場合、その長さを計算すると評価が強制され、問題が発生する可能性があります。リストが既にメモリにある場合は、それほど問題にはなりません。)
編集:実際にパーセンテージを表示する前のコードの可能な拡張は次のとおりです。
{-# LANGUAGE FlexibleContexts #-}
import Pipes
import qualified Pipes.Prelude as P
import Data.Function
import Control.Monad.RWS
bigList :: [Int]
bigList = [1,2,3,4]
check :: Int -> Bool
check = (>3)
-- List length is the environment, number of received tasks is the state.
tracker :: (MonadReader Int m, MonadState Int m, MonadIO m) => Pipe a a m r
tracker = P.chain $ \_ -> do
progress <- on (/) fromIntegral `liftM` (modify succ >> get) `ap` ask
liftIO . putStrLn . show $ progress
main :: IO ()
main = do
(result,()) <- evalRWST (P.any check $ each bigList >-> tracker)
(length bigList) -- list length as unchanging environment
0 -- initial number of received tasks (the mutable state)
putStrLn . show $ result
大幅な増加率のみを表示するようにさらに絞り込むことができます。
explicit-exception:Control.Monad.Execption.Synchronous
チェックをパスする要素を見つけたときに、またはtransformers:Control.Monad.Trans.Maybe
「例外をスローする」などの明示的な例外にライブラリを使用できます。