我想这里的每个人都已经看过其中一个(或者至少是一个类似的)问题,我仍然需要问,因为我无法在任何地方找到这个问题的答案(主要是因为我不知道到底应该看到什么)对于)
我写了这个小脚本,其中printTriangle应该打印出pascal三角形.
fac = product . enumFromTo 2
binomial n k = (product (drop (k-1) [2..n])) `div` (fac (n-k))
pascalTriangle maxRow =
do row<-[0..maxRow-1]
return (binomialRow row)
where
binomialRow row =
do k<-[0..row]
return (binomial row k)
printTriangle :: Int -> IO ()
printTriangle rows = do row<-(triangle)
putStrLn (show row)
where
triangle = pascalTriangle rows
Run Code Online (Sandbox Code Playgroud)
现在由于受过训练的眼睛可能显而易见的原因,但对我来说完全笼罩在神秘之中,当我尝试在ghci中加载时会出现以下错误:
Couldn't match expected type `IO t0' with actual type `[[Int]]'
In a stmt of a 'do' expression: row <- (triangle)
In the expression:
do { row <- (triangle);
putStrLn (show row) }
In
an equation for `printTriangle':
printTriangle rows
= do { row <- (triangle);
putStrLn (show row) }
where
triangle = pascalTriangle rows
Run Code Online (Sandbox Code Playgroud)
我试图做的是像我这样调用printTriangle:
printTriangle 3
Run Code Online (Sandbox Code Playgroud)
我得到这个输出:
[1]
[1,1]
[1,2,1]
Run Code Online (Sandbox Code Playgroud)
如果有人能够向我解释为什么我在这里做的事情不起作用(说实话,我不确定我在这里做了什么;我已经习惯了命令式语言,这整个函数编程的东西仍然令人困惑对我而言,以及如何以一种不那么愚蠢的方式做到这一点.
提前致谢.
你在评论中说你认为列表是monad,但现在你不确定 - 嗯,你是对的,列表是 monad!那么为什么你的代码不起作用呢?
好吧,因为IO也是一个单子.因此,当编译器看到printTriangle :: Int -> IO (),然后编写符号时,它会说"啊哈!我知道该怎么做!他正在使用IO monad!" 尝试想象它的冲击和绝望,当它发现而不是IO monads,它发现里面的列表单子!
这就是问题:打印和处理外部世界,你需要使用IO monad; 在函数内部,您尝试使用列表作为monad.
让我们看看这是一个什么问题.do-notation是Haskell的语法糖,用来引诱我们进入它的蛋糕屋并吃掉我们....我的意思是它的语法糖>>=(发音为bind)引诱我们使用monads(并享受它).所以让我们printTriangle用bind 编写:
printTriangle rows = (pascalTriangle rows) >>= (\row ->
putStrLn $ show row)
Run Code Online (Sandbox Code Playgroud)
好的,这很简单.现在我们看到有什么问题吗?好吧,让我们来看看类型.绑定的类型是什么?霍格尔说:(>>=) :: Monad m => m a -> (a -> m b) -> m b.好的,谢谢Hoogle.所以基本上,bind需要一个monad类型包装一个类型的个性,一个将一个类型转换为一个类型的函数(相同)包含一个b型个性的monad类型,并以包含一个类型的(相同)monad类型结束 - b个性.
所以在我们这里printTriangle,我们有什么?
pascalTriangle rows :: [[Int]]- 所以我们的monad是[],而且个性是[Int](\row -> putStrLn $ show row) :: [Int] -> IO () - 这里的monad是IO,个性是 ()好吧,废话.当Hoogle告诉我们必须匹配我们的monad类型时,Hoogle非常清楚,相反,我们给出>>=了一个列表monad,以及一个生成IO monad的函数.这使得Haskell表现得像个小孩子:它闭上眼睛,在地板上st脚尖叫"不!不!不!" 甚至不会看你的程序,更不用说编译它了.
那么我们如何安抚Haskell呢?好吧,其他人已经提到了mapM_.向顶级函数添加显式类型签名也是一个好主意 - 它有时可以帮助您更快地获得编译错误,而不是以后(并且你会得到它们;毕竟这是Haskell :)),这使得它更容易理解错误消息.
我建议写一个函数将你[[Int]]变成一个字符串,然后单独打印出来.通过将转换分离为IO-nastiness中的字符串,这将允许您继续学习Haskell而不必担心mapM_和朋友,直到您做好并准备好.
showTriangle :: [[Int]] -> String
showTriangle triangle = concatMap (\line -> show line ++ "\n") triangle
Run Code Online (Sandbox Code Playgroud)
要么
showTriangle = concatMap (\line -> show line ++ "\n")
Run Code Online (Sandbox Code Playgroud)
然后printTriangle更容易:
printTriangle :: Int -> IO ()
printTriangle rows = putStrLn (showTriangle $ pascalTriangle rows)
Run Code Online (Sandbox Code Playgroud)
要么
printTriangle = putStrLn . showTriangle . pascalTriangle
Run Code Online (Sandbox Code Playgroud)