Haskell 哈斯克尔:如何计数

Haskell 哈斯克尔:如何计数,haskell,counter,io-monad,Haskell,Counter,Io Monad,我以一种传统的方式递归地遍历一个目录。这是一个正在工作的原型: traverseFlatDst :: FilePath -> Int -> Int -> FilePath -> IO () traverseFlatDst dstRoot total totw srcDir = do (dirs, files) <- listDir srcDir mapM_ (\file -> putStrLn (printf "%s" (strp file))) fi

我以一种传统的方式递归地遍历一个目录。这是一个正在工作的原型:

traverseFlatDst :: FilePath -> Int -> Int -> FilePath -> IO ()
traverseFlatDst dstRoot total totw srcDir = do
  (dirs, files) <- listDir srcDir
  mapM_ (\file -> putStrLn (printf "%s" (strp file))) files    -- tracing
  let traverse = traverseFlatDst dstRoot total totw
  mapM_ traverse dirs
traverseFlatDst::FilePath->Int->Int->FilePath->IO()
traverseFlatDst dstRoot总计totw srcDir=do
(目录,文件)putStrLn(printf“%s”(strp文件)))文件--跟踪
let traverse=traverseFlatDst dstRoot总计totw
mapM_uuu导线定向器
我有一个不太寻常的要求:每个跟踪行都应该编号(它实际上不是用于跟踪的)。像这样:

traverseFlatDst :: FilePath -> Int -> Int -> FilePath -> IO ()
traverseFlatDst dstRoot total totw srcDir = do
  (dirs, files) <- listDir srcDir
  mapM_ (\file -> putStrLn (printf "%d: %s" counterFromNowhere (strp file))) files
  let traverse = traverseFlatDst dstRoot total totw
  mapM_ traverse dirs
traverseFlatDst::FilePath->Int->Int->FilePath->IO()
traverseFlatDst dstRoot总计totw srcDir=do
(目录,文件)putStrLn(printf“%d:%s”文件(strp文件)))文件
let traverse=traverseFlatDst dstRoot总计totw
mapM_uuu导线定向器

到目前为止,我看到的所有解决方案都是难以想象的丑陋,如果适用的话。有什么好办法管理它吗?

不知从哪里来?当然不是

您可以用数字压缩
文件
,然后将
mapM
放在上面:

mapM_ (\(file, counter) -> putStrLn (printf "%d: %s" counter (strp file))) (zip [0..] files)

不知从哪里来?当然不是

您可以用数字压缩
文件
,然后将
mapM
放在上面:

mapM_ (\(file, counter) -> putStrLn (printf "%d: %s" counter (strp file))) (zip [0..] files)

你可以通过给你的功能增加一个额外的效果来实现这一点;即国家效应

import Control.Monad.State

printPath :: (PrintfArg t, Show a) => (t, a) -> IO ()
printPath (l, file) = printf "%d : %s\n" l (show file)

traverseFlatDst :: Path Abs Dir -> IO ()
traverseFlatDst =
  let loop srcDir = do
        (dirs, files) <- listDir srcDir
        i <- get
        put (i + length files)
        mapM_ (liftIO . printPath) $ zip [i..] files
        mapM_ loop dirs
  in \s -> evalStateT (loop s) 0
还要注意的是,第二个版本比第一个版本严格得多;在开始打印任何内容之前,它将遍历整个目录树。一般来说,严格版本更好,但如果您想要懒版本,可以使用
unsafeInterleaveIO

import System.IO.Unsafe (unsafeInterleaveIO)

traverseFlatDst' :: Path Abs Dir -> IO [Path Abs File]
traverseFlatDst' srcDir = do
  (dirs, files) <- listDir srcDir
  files' <- unsafeInterleaveIO $ mapM traverseFlatDst' dirs
  return $ concat $ files:files'
import System.IO.Unsafe(unsafeInterleaveIO)
traverseFlatDst'::路径Abs目录->IO[路径Abs文件]
traverseFlatDst'srcDir=do

(dirs,files)您可以通过在函数中添加附加效果来实现这一点;即国家效应

import Control.Monad.State

printPath :: (PrintfArg t, Show a) => (t, a) -> IO ()
printPath (l, file) = printf "%d : %s\n" l (show file)

traverseFlatDst :: Path Abs Dir -> IO ()
traverseFlatDst =
  let loop srcDir = do
        (dirs, files) <- listDir srcDir
        i <- get
        put (i + length files)
        mapM_ (liftIO . printPath) $ zip [i..] files
        mapM_ loop dirs
  in \s -> evalStateT (loop s) 0
还要注意的是,第二个版本比第一个版本严格得多;在开始打印任何内容之前,它将遍历整个目录树。一般来说,严格版本更好,但如果您想要懒版本,可以使用
unsafeInterleaveIO

import System.IO.Unsafe (unsafeInterleaveIO)

traverseFlatDst' :: Path Abs Dir -> IO [Path Abs File]
traverseFlatDst' srcDir = do
  (dirs, files) <- listDir srcDir
  files' <- unsafeInterleaveIO $ mapM traverseFlatDst' dirs
  return $ concat $ files:files'
import System.IO.Unsafe(unsafeInterleaveIO)
traverseFlatDst'::路径Abs目录->IO[路径Abs文件]
traverseFlatDst'srcDir=do

(dirs,files)我可能会使用流媒体库,将枚举文件与添加数字和打印装饰条目分开:

import Streaming
import qualified Streaming.Prelude as S

traverseFlatDst :: FilePath -> Int -> Int -> FilePath -> Stream (Of FilePath) IO ()
traverseFlatDst dstRoot total totw srcDir = do
  (dirs, files) <- liftIO $ listDir srcDir
  S.each files
  mapM_ (traverseFlatDst dstRoot total totw) dirs

decorate :: Stream (Of FilePath) IO r -> Stream (Of (Int,FilePath)) IO r
decorate stream = S.zip (S.enumFrom 1) stream

display:: Stream (Of (Int,FilePath)) IO () -> IO ()
display = S.mapM_ $ \(index,path) ->  putStrLn $ show index ++ " " ++ path
导入流媒体
导入符合条件的流媒体。前奏为S
traverseFlatDst::FilePath->Int->Int->FilePath->Stream(文件路径的)IO()
traverseFlatDst dstRoot总计totw srcDir=do
(dirs,files)流(Of(Int,FilePath))IO r
装饰流=S.zip(S.enumFrom 1)流
display::Stream(Of(Int,FilePath))IO()->IO()
display=S.mapM(索引,路径)->putStrLn$show index++++++path

其中,来自流媒体。

我可能会使用流媒体库,将枚举文件与添加数字和打印装饰条目分开:

import Streaming
import qualified Streaming.Prelude as S

traverseFlatDst :: FilePath -> Int -> Int -> FilePath -> Stream (Of FilePath) IO ()
traverseFlatDst dstRoot total totw srcDir = do
  (dirs, files) <- liftIO $ listDir srcDir
  S.each files
  mapM_ (traverseFlatDst dstRoot total totw) dirs

decorate :: Stream (Of FilePath) IO r -> Stream (Of (Int,FilePath)) IO r
decorate stream = S.zip (S.enumFrom 1) stream

display:: Stream (Of (Int,FilePath)) IO () -> IO ()
display = S.mapM_ $ \(index,path) ->  putStrLn $ show index ++ " " ++ path
导入流媒体
导入符合条件的流媒体。前奏为S
traverseFlatDst::FilePath->Int->Int->FilePath->Stream(文件路径的)IO()
traverseFlatDst dstRoot总计totw srcDir=do
(dirs,files)流(Of(Int,FilePath))IO r
装饰流=S.zip(S.enumFrom 1)流
display::Stream(Of(Int,FilePath))IO()->IO()
display=S.mapM(索引,路径)->putStrLn$show index++++++path

其中,来自流媒体。

最终解决方案,借用自

import Data.IORef
类型计数器=Int->IO Int
makeCounter::IO计数器
makeCounter=do
r do modifyIORef r(+i)
readIORef(r)
打印路径::计数器->文件路径->IO()
printPath计数器文件=do
n Int->Int->Counter->FilePath->IO()
traverseFlatDst dstRoot总计总计cnt srcDir=do
(目录、文件)文件路径->Int->IO()
梳理src dst总计=do

计数器最终解决方案,借用自

import Data.IORef
类型计数器=Int->IO Int
makeCounter::IO计数器
makeCounter=do
r do modifyIORef r(+i)
readIORef(r)
打印路径::计数器->文件路径->IO()
printPath计数器文件=do
n Int->Int->Counter->FilePath->IO()
traverseFlatDst dstRoot总计总计cnt srcDir=do
(目录、文件)文件路径->Int->IO()
梳理src dst总计=do

计数器此解决方案不需要额外的库,在找到每个文件时即对其进行处理,并且为了分离关注点,不需要
traverseFlatDst
了解对生成的文件所做的操作

最后一个功能是通过将一个小型有效状态机(实际上是一个步长函数)作为参数传递给
traverseFlatDst
,并使
traverseFlatDst
在机器状态下具有多态性来实现的,因此它对它一无所知:

{-# language RankNTypes #-}
import Control.Monad (foldM)

type Source e = forall s. (s -> e -> IO s) -> s -> IO s

traverseFlatDst :: FilePath -> Int -> Int -> FilePath -> Source FilePath
traverseFlatDst dstRoot total totw srcDir step state = do
  (dirs, files) <- listDir srcDir
  state' <- foldM step state files
  foldM (\s path -> traverseFlatDst dstRoot total totw path step s) state' dirs

-- Pass this as the step argument to traverseFlatDst
-- The counter is the state.
step :: Int -> FilePath -> IO Int
step index path = do
    putStrLn $ show index ++ " " ++ path
    return $ succ index
{-#语言等级}
进口管制.单子(foldM)
输入Source e=forall s。(s->e->IO s)->s->IO s
traverseFlatDst::FilePath->Int->Int->FilePath->源文件路径
traverseFlatDst dstRoot总计totw srcDir步骤状态=do
(目录、文件)文件路径->IO Int
步骤索引路径=do
putStrLn$show index++++++path
返回$succ索引

此解决方案不需要额外的库,在找到每个文件时即对其进行处理,并且为了分离关注点,不需要
traverseFlatDst
了解对生成的文件所做的操作

最后一个功能是通过将一个小型有效状态机(实际上是一个步长函数)作为参数传递给
traverseFlatDst
,并使
traverseFlatDst
在机器状态下具有多态性来实现的,因此它对它一无所知:

{-# language RankNTypes #-}
import Control.Monad (foldM)

type Source e = forall s. (s -> e -> IO s) -> s -> IO s

traverseFlatDst :: FilePath -> Int -> Int -> FilePath -> Source FilePath
traverseFlatDst dstRoot total totw srcDir step state = do
  (dirs, files) <- listDir srcDir
  state' <- foldM step state files
  foldM (\s path -> traverseFlatDst dstRoot total totw path step s) state' dirs

-- Pass this as the step argument to traverseFlatDst
-- The counter is the state.
step :: Int -> FilePath -> IO Int
step index path = do
    putStrLn $ show index ++ " " ++ path
    return $ succ index
{-#语言等级}
进口管制.单子(foldM)
输入Source e=forall s。(s->e->IO s)->s->IO s
traverseFlatDst::FilePath->Int->Int->FilePath->