あなたの質問を解決するために3つの別々のトリックを使用します。
- トリック1:ツリーをトラバースすると同時に、ファイル名をストリーミングする
pipes
ライブラリを使用してください。
- トリック2:幅優先トラバーサルを実現するには、
StateT (Seq FilePath)
トランスフォーマーを使用します。
- トリック3:
MaybeT
トランスフォーマを使用して、ループを書き込んで終了するときに手動再帰を回避します。
次のコードは、これらの3つのトリックを1つのモナドトランススタックで組み合わせています。
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Maybe
import Control.Monad.State.Lazy
import Control.Pipe
import Data.Sequence
import System.FilePath.Posix
import System.Directory
loop :: (Monad m) => MaybeT m a -> m()
loop = liftM (maybe() id) . runMaybeT . forever
quit :: (Monad m) => MaybeT m a
quit = mzero
getUsefulContents :: FilePath -> IO [FilePath]
getUsefulContents path
= fmap (filter (`notElem` [".", ".."])) $ getDirectoryContents path
permissible :: FilePath -> IO Bool
permissible file
= fmap (\p -> readable p && searchable p) $ getPermissions file
traverseTree :: FilePath -> Producer FilePath IO()
traverseTree path = (`evalStateT` empty) $ loop $ do
-- All code past this point uses the following monad transformer stack:
-- MaybeT (StateT (Seq FilePath) (Producer FilePath IO))()
let liftState = lift
liftPipe = lift . lift
liftIO = lift . lift . lift
liftState $ modify (|> path)
forever $ do
x <- liftState $ gets viewl
case x of
EmptyL -> quit
file :< s -> do
liftState $ put s
liftPipe $ yield file
p <- liftIO $ doesDirectoryExist file
when p $ do
names <- liftIO $ getUsefulContents file
-- allowedNames <- filterM permissible names
let namesfull = map (path </>) names
liftState $ forM_ namesfull $ \name -> modify (|> name)
これは、ツリートラバーサルと同時に消費することができ幅優先ファイル名のジェネレータを作成します。
さらに重要な
-- Demand only 'n' elements
take' :: (Monad m) => Int -> Pipe a a m()
take' n = replicateM_ n $ do
a <- await
yield a
>> runPipe $ printer <+< take' 3 <+< traverseTree path
<Prints only three files>
、最後の例では、3つだけを生成することと同じくらい必要に応じてツリーを走査すること:あなたもすべての値を要求しないことを選択することができ
printer :: (Show a) => Consumer a IO r
printer = forever $ do
a <- await
lift $ print a
>>> runPipe $ printer <+< traverseTree path
<Prints file names as it traverses the tree>
を:あなたが使用して値を消費しますファイルを削除してから停止します。これは、あなたが望むすべてが3つの結果であったときに木全体を無駄に横切ることを防ぎます!
pipes
ライブラリーのトリックについて詳しくは、pipes tutorial(Control.Pipes.Tutorial
)を参照してください。
ループトリックについて詳しくは、blog postをお読みください。
広さの最初のトラバーサルでキュートリックの良いリンクを見つけることができませんでしたが、どこかにあることはわかっています。他の誰かがこれに対する良いリンクを知っている場合は、自分の答えを編集して追加してください。
ご利用いただきありがとうございます。パイプを理解することは大きな助けになります。私は導管について読んでいましたが、それを使用する予定でしたが、私は最初にツリートラバーサルのための単純な遅延ソリューションを用意する必要があります。 私はそれを試して、それは動作しますが、それは木を再帰しません、そして、あなたのコードでどこに再帰するのか分かりません。 欠落しているコードが除外されています。と ".."ディレクトリのリストから getUsefulContents path = do 名前< - getDirectoryContents path return(フィルター( 'notElem' ["。 "、" .. "])) – user855443
私はより深い点検で新しいファイル名が "todo"リストに追加される、リフトステートを持つ最後の行の再帰的な再帰的な再帰的な再帰呼び出しです。 コードは追加されたファイルの完全なファイルパスを生成しないので、私はこれを見ませんでした。 pathの値は元の開始値であり、毎回現在のファイル名に設定されません - > pathとfileを置き換えれば動作します。 が完全に動作するには、私は のgetInfo :: FilePath - > IO Info で実際に行ったことのあるディレクトリのアクセス権をチェックする必要があります。 – user855443
これは難しい問題です。リンクをフィルタリングするテストを追加する必要があります。 これは動作し、すべての4つのコアを使用します。メモリが不足するまで、使用状況が非常に遅くなるため、メモリリークが発生します。あなたはどこを見ることができますか? あなたの助けが大変ありがとうございました。それは、木をたどるときにパイプを使う方法の良い実践的な例が必要でした! – user855443