【问题标题】:List of IO StringsIO 字符串列表
【发布时间】:2014-03-06 02:29:08
【问题描述】:

我是 Haskell 和 FP 的新手,所以这个问题可能看起来很愚蠢。

我的主函数中有一行代码

let y = map readFile directoryContents

directoryContents 的类型为[FilePath]。这反过来(我认为)使 y 键入 [IO String] ,因此是一个字符串列表 - 每个字符串都包含 directoryContents 中每个文件的内容。

我在另一个模块中编写了一个函数,可以在 [String]String 上运行,但我现在不清楚如何调用/使用它们,因为 y 的类型是 [IO String]。有什么指点吗?


编辑:

有人建议我使用mapM 而不是map,所以:

let y = mapM readFile directoryContentsy 现在输入 IO [String],从这里我该怎么办?

【问题讨论】:

标签: haskell io


【解决方案1】:

你没看错,类型是y :: [IO String]

嗯,这里基本上有两个主要部分:

如何将[IO String]变成IO [String]

[IO String]IO 操作的列表,我们需要的是一个携带字符串列表(即IO [String])的IO 操作。幸运的是,函数序列正好提供了我们所需要的:

sequence :: Monad m => [m a] -> m [a]
y' = sequence y :: IO [String]

现在mapM函数可以简化这一点,我们可以将y'改写为:

y' = mapM readFile directoryContents

mapM 为我们完成序列。

如何获得 [String]

我们的类型现在是IO [String],所以现在的问题是“我们如何从 IO 中取出 [String]?”这就是函数>>= (bind) 的作用:

(>>=) :: Monad m => m a -> (a -> m b) -> m b
  -- Specialized to IO, that type is:
(>>=) :: IO a -> (a -> IO b) -> IO b

我们还有一个函数return :: Monad m => a -> m a,它可以将值“放入”IO

所以有了这两个函数,如果我们有一些函数f :: [String] -> SomeType,我们可以这样写:

ourResult = y' >>= (\theStringList -> return (f theStringList))  :: IO SomeType

函数可以与>>= 函数“链接”在一起。有时这可能有点难以理解,因此 Haskell 提供了do 符号以使事情在视觉上更简单:

ourResult = do
  theStringList <- y'
  return $ f theStringList

编译器在内部将其转换为y' &gt;&gt;= (\theStringList -&gt; f theStringList),与我们之前的y' &gt;&gt;= f 相同。

把它们放在一起

我们实际上可能不希望 y' 到处乱跑,所以我们可以消除它并得出:

ourResult = do
  theStringList <- mapM readFile directoryContents
  return $ f theStringList

更加简化

事实证明,这实际上并不需要&gt;&gt;= 的全部功能。事实上,我们只需要fmap!这是因为函数 f 只有一个“内部”参数 IO 并且我们没有使用任何其他以前的 IO 结果:我们正在生成结果然后立即使用它。

使用法律

fmap f xs  ==  xs >>= return . f

我们可以像这样重写&gt;&gt;= 代码来使用fmap:

ourResult = fmap f (mapM readFile directoryContents)

如果我们想要更简洁,fmap 的中缀同义词称为&lt;$&gt;

ourResult = f <$> mapM readFile directoryContents

【讨论】:

  • 感谢您的解释!如果您不介意,最后一件事有点无关紧要:directoryContents 包含文件“..”和“.”并且我认为当 readFile 尝试打开“..”和“.”时出现 openFile 错误。知道如何解决这个问题吗?
  • 您可以使用drop 2drop 2 [".", "..", "a", "b", "c"] == ["a", "b", "c"]
  • 一个很好且全面的解释,但我将fmap 更改为liftM 以免混淆,因为fmap 是为函子定义的,而&gt;&gt;=liftM用于单子。哎呀,我们仍然没有Monad 作为Functor 的子类:(
  • @user3369427 我还建议在使用filterM doesFileExist 阅读之前过滤FilePaths 的列表,因为如果您的目录中有一些子文件夹,当前代码也会出现问题。
猜你喜欢
  • 2011-10-01
  • 2021-10-18
  • 1970-01-01
  • 1970-01-01
  • 2011-08-22
  • 2014-03-14
  • 2013-04-09
  • 1970-01-01
  • 2013-05-01
相关资源
最近更新 更多