我编写了一个Haskell函数,用于计算给定列表中每个数字的阶乘,并将其打印到屏幕上。
factPrint list =
if null list
then putStrLn ""
else do putStrLn ((show.fact.head) list)
factPrint (tail list)
该功能有效,但我发现第三行有点令人困惑。 为什么编译器(GHC)没有报告错误,因为在“putStrLn”(准?)函数之前没有“do”? 如果我从第4行省略“do”,则会按预期弹出错误。
我对Haskell及其方式都很陌生,所以如果我说的话过于愚蠢,请原谅我。
答案 0 :(得分:11)
do putStrLn ((show.fact.head) list)
factPrint (tail list)
实际上是另一种写作方式
putStrLn ((show.fact.head) list) >> factPrint (tail list)
反过来意味着
putStrLn ((show.fact.head) list) >>= \_ -> factPrint (tail list)
do
符号是将这些monad串在一起的便捷方式,没有其他丑陋的语法。
如果do
中只有一个声明,那么你就不会将任何内容串起来,do
是多余的。
答案 1 :(得分:5)
如果您是Haskell的新手,请将do
视为类似C语言if
之后的必需大括号:
if (condition)
printf("a"); // braces not required
else {
printf("b"); // braces required
finish();
}
do
在Haskell中的工作方式相同。
也许有助于查看factPrint的类型,然后重构使用模式匹配:
factPrint :: [Int] -> IO ()
factPrint [] = putStrLn ""
factPrint list = do
putStrLn (show.fact.head) list
factPrint (tail list)
因此,如果factPrint返回IO ()
,而putStrLn ""
的类型为IO ()
,则factPrint []
等于putStrLn ""
是完全合法的。不需要do
- 事实上,如果您不想要跟踪换行符,则可以说factPrint [] = return ()
。
答案 2 :(得分:4)
do
用于将多个monadic表达式绑定在一起。只有一个表达式后,它没有任何效果。
对于if格式良好,只需要then子句和else子句具有相同的类型。由于这两个子句的类型都为IO ()
,因此就是这种情况。
答案 3 :(得分:3)
do关键字用于排序,如果每个分支都是单个语句,则Haskell中的if-then-else根本不需要包含do
,例如。
if a
then b
else c
在您的示例中需要do
,因为您在其他分支上对两个操作进行了排序。如果省略do
,那么factPrint(tail list)
语句被认为不属于该函数,因此编译器会因为遇到意外语句而抱怨。