【发布时间】:2017-03-03 08:18:37
【问题描述】:
我正在学习 Haskell,并编写一个简短的解析脚本作为练习。我的大部分脚本由纯函数组成,但我有两个嵌套的 IO 组件:
- 从路径读取文件列表。
- 读取每个文件的内容,而这些内容又将成为程序其余部分的输入。
我有什么工作,但是嵌套 IO 和 fmap 层“感觉”很笨重,就像我应该避免嵌套 IO(不知何故),或者更巧妙地使用 do 表示法来避免所有 fmap。我想知道我是否过于复杂,做错了等等。这里有一些相关的代码:
getPaths :: FilePath -> IO [String]
getPaths folder = do
allFiles <- listDirectory folder
let txtFiles = filter (isInfixOf ".txt") allFiles
paths = map ((folder ++ "/") ++) txtFiles
return paths
getConfig :: FilePath -> IO [String]
getConfig path = do
config <- readFile path
return $ lines config
main = do
paths = getPaths "./configs"
let flatConfigs = map getConfigs paths
blockConfigs = map (fmap chunk) flatConfigs
-- Parse and do stuff with config data.
return
我最终通过使用 listDirectory 作为 readFile 的输入来处理 IO [IO String]。并非无法管理,但如果我使用 do 表示法解开 [IO String] 以发送到某个解析器函数,我最终还是会使用嵌套的 fmap 或污染我所谓的纯函数与 IO 感知(fmap 等)。后者似乎更糟,所以我正在做前者。示例:
type Block = [String]
getTrunkBlocks :: [Block] -> [Block]
getTrunkBlocks = filter (liftM2 (&&) isInterface isMatchingInt)
where isMatchingInt line = isJust $ find predicate line
predicate = isInfixOf "switchport mode trunk"
main = do
paths <- getPaths "./configs"
let flatConfigs = map getConfig paths
blockConfigs = map (fmap chunk) flatConfigs
trunks = fmap (fmap getTrunkBlocks) blockConfigs
return $ "Trunk count: " ++ show (length trunks)
fmap、fmap、fmap...我觉得我无意中让这变得比必要的更复杂,并且无法想象如果我有更深的 IO 嵌套会变得多么复杂。
建议?
提前致谢。
【问题讨论】:
-
嗨 - 欢迎来到 stackoverflow 和 haskell!恭喜你,我认为你是发现
(>>=)有用性和 monad 概念的最佳方式,请参阅 this blog post -
还有
sequence或traverse=mapM可能有用 -
join . fmap sequence :: IO [IO a] -> IO [a]很短。但似乎你只是想要,例如mapM getConfigs,首先。
标签: haskell