writeStr []=putChar ' '
writeStr (x:xs) = (putChar x)(writeStr xs)
您好,并提前感谢,我收到类型错误,它应该是一个简单的答案,但我只是不知道错误来自哪里。
答案 0 :(得分:4)
你的代码有点奇怪。如果我做对了,你会尝试打印一个字符串。你的方法是放第一个字符串,而不是第二个字符串。但是在Haskell中不可能将这样的两个IO动作组合在一起。再看一下你的教程,这里有两个看起来像:
writeStr [] = return () -- you had putChar ' ',
writeStr (x:xs) = do putChar x -- but this would print a superfluous whtiespace
writeStr xs
如果您想按顺序执行多项操作,请使用do-keyword或monadic组合器。它非常简单,就像这样:
do action1
action2
action3
...
答案 1 :(得分:3)
FUZxxl回答了当前的问题,但是我想通过更多方式编写“writeStr”来扩展它,以更多地了解monad。
正如德尔南在评论中所说,你也可以写
writeStr [] = return ()
writeStr (x:xs) = putChar x >> writeStr xs
这实际上是“do”符号的“desugared”版本。 “>>”运算符用于将monadic动作串联起来。它实际上是“绑定”运算符的专用版本,写成“>> =”。有关详细信息,请参阅this question。
但是当你看到这一点时,似乎我们所做的只是将“putChar”应用于参数列表中的每个元素。 Prelude中已经有一个名为“map”的函数用于执行此操作,所以也许我们可以编写:
writeStr xs = map putChar xs
但是当你尝试它时,它将无法工作。如果你进入GHCi并输入以下内容,原因就显而易见了:
:type map putChar "Hello"
[IO ()]
您需要单个“IO()”操作,但这会为您提供一个列表。您需要的是一个将此IO操作列表转换为单个IO操作的函数。幸运的是存在一个。 Prelude包含这两个函数
sequence :: [IO a] -> IO [a]
sequence_ :: [IO a] -> IO ()
第一个用于何时需要结果列表,第二个用于不需要的情况,例如这个。 (在这个答案中,为了清楚起见,我将提供IO特定类型的签名,但重要的是要记住所有这些函数实际上适用于任何 monad。)
现在你可以写:
writeStr xs = sequence_ $ map putChar xs
但有一种方法可以缩短这一点。回想一下“。”运算符,它将两个函数组合在一起,以及Haskell的“currying”函数参数的方式?我们可以将上面的函数重写为:
writeStr = sequence_ . map putChar
这种“无点”风格起初看起来很奇怪;它使“writeStr”看起来更像是一个常量而不是一个函数。但它避免了在阅读代码时跟踪代码周围的变量名称的需要,因此通常是首选。当你把一些复杂的东西作为“映射”或类似的高阶函数的参数时,它也更短更易读。
但我们可以更短。 “sequence.map f”模式非常常见,因此“Control.Monad”模块定义了更多功能来体现它:
mapM :: (a -> IO b) -> [a] -> IO [b]
mapM f = sequence . map f
mapM_ :: (a -> IO b) -> [a] -> IO ()
mapM_ f = sequence_ . map f
所以你最后可以写
writeStr = mapM_ putChar