我对Haskell缺乏经验,我想提高自己的水平,因此对于我的学习项目,我有以下要求:
.md
。toplevel/.excluded
。.filename.md.swp
之类的隐藏文件。我搜索了整个SO。这是我到目前为止的内容:
import qualified System.FilePath.Find as SFF
import qualified Filesystem.Path.CurrentOS as FP
srcFolderName = "src"
outFolderName = "output"
resFolderName = "res"
ffNotHidden :: SFF.FindClause Bool
ffNotHidden = SFF.fileName SFF./~? ".?*"
ffIsMD :: SFF.FindClause Bool
ffIsMD = SFF.extension SFF.==? ".md" SFF.&&? SFF.fileName SFF./~? ".?*"
findMarkdownSources :: FilePath -> IO [FilePath]
findMarkdownSources filePath = do
paths <- SFF.find ffNotHidden ffIsMD filePath
return paths
这不起作用。在“ findMarkdownSources”中进行printf样式的调试后,我可以验证filePath是否正确,例如"/home/user/testdata"
(打印出来的文件中包含“,以防万一。)列表paths
始终为空。我绝对确定我指定的目录中有markdown文件(查找/ path / to / dir -name“ * .md”找到它们)。
因此,我有一些具体问题。
haskell中有多种方法可以执行此操作。似乎至少有六个软件包(fileman,system.directory,system.filepath.find)专用于此。这是回答以下问题的一些问题:
每个人都有三种独特的方式来实现我想要的目标,因此,我们几乎有10种方式来实现目标...
如果有帮助,我对基本的haskell会比较满意,但是如果我们开始对单子和应用函子开始过于沉重,您就需要放慢速度(我没有足够地使用haskell来保持头脑清醒)。不过,我发现有关Hackage的Haskell文档令人难以理解。
答案 0 :(得分:5)
所以,我们差不多有10种方法可以做到这一点...
这是使用directory,filepath和extra软件包中的函数的另一种方法,但不要使用太多monad向导:
import Control.Monad (foldM)
import System.Directory (doesDirectoryExist, listDirectory) -- from "directory"
import System.FilePath ((</>), FilePath) -- from "filepath"
import Control.Monad.Extra (partitionM) -- from the "extra" package
traverseDir :: (FilePath -> Bool) -> (b -> FilePath -> IO b) -> b -> FilePath -> IO b
traverseDir validDir transition =
let go state dirPath =
do names <- listDirectory dirPath
let paths = map (dirPath </>) names
(dirPaths, filePaths) <- partitionM doesDirectoryExist paths
state' <- foldM transition state filePaths -- process current dir
foldM go state' (filter validDir dirPaths) -- process subdirs
in go
这个想法是用户通过FilePath -> Bool
函数来过滤不需要的目录。还有一个初始状态b
和一个转换函数b -> FilePath -> IO b
,用于处理文件名,更新b
状态,并且可能会有一些副作用。请注意,状态的类型由调用者选择,调用者可以在其中放置有用的内容。
如果我们只想打印文件名,则可以执行以下操作:
traverseDir (\_ -> True) (\() path -> print path) () "/tmp/somedir"
我们将()
用作虚拟状态,因为我们在这里实际上并不需要它。
如果我们想将文件累积到一个列表中,可以这样做:
traverseDir (\_ -> True) (\fs f -> pure (f : fs)) [] "/tmp/somedir"
如果我们要过滤一些文件怎么办?我们需要调整传递给traverseDir
的转换函数,以使其忽略它们。
答案 1 :(得分:2)
我在您的计算机上测试了您的代码,它似乎可以正常工作。这是一些示例数据:
$ find test/data
test/data
test/data/look-a-md-file.md
test/data/another-dir
test/data/another-dir/shown.md
test/data/.not-shown.md
test/data/also-not-shown.md.bkp
test/data/.hidden
test/data/some-dir
test/data/some-dir/shown.md
test/data/some-dir/.ahother-hidden
test/data/some-dir/.ahother-hidden/im-hidden.md
运行功能会导致:
ghci> findMarkdownSources "test"
["test/data/another-dir/shown.md","test/data/look-a-md-file.md","test/data/some-dir/shown.md"]
我已经用绝对路径测试了它,它也可以工作。您确定您通过了有效路径吗?在这种情况下,您会得到一个空列表(尽管您也会收到警告)。
请注意,您的代码可以简化如下:
module Traversals.FileManip where
import Data.List (isPrefixOf)
import System.FilePath.Find (always, extension, fileName, find, (&&?),
(/~?), (==?))
findMdSources :: FilePath -> IO [FilePath]
findMdSources fp = find isVisible (isMdFile &&? isVisible) fp
where
isMdFile = extension ==? ".md"
isVisible = fileName /~? ".?*"
您甚至可以删除fp
参数,但是为了清楚起见,我将其保留在此处。
我更喜欢显式导入,以便知道每个函数的来源(因为我不知道任何具有高级符号导航的Haskell IDE)。
但是,请注意,该解决方案使用了is not recommended的不安全交错IO。
因此,关于您的问题2和3,我建议使用流式解决方案,例如pipes或管道。坚持使用此类解决方案将减少您的选择(就像坚持使用纯函数式编程语言会减少我对编程语言的选择一样;)。 Here上有一个示例,说明了如何使用管道来遍历目录。
Here是您想尝试的代码。