Haskell初学者,试图输出一个列表 [英] Haskell beginner, trying to output a list

查看:107
本文介绍了Haskell初学者,试图输出一个列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想这里的每个人都已经看过其中的一个(或者至少是类似的)问题,但我仍然需要问,因为我无法在任何地方找到这个问题的答案(主要是因为我不知道我究竟是什么应该是寻找)



我写了这个小脚本,其中printTriangle应该打印出pascal三角形。

  fac =产品。 enumFromTo 2 

binomial nk =(product(drop(k-1)[2..n]))`div`(fac(nk))

pascalTriangle maxRow =
do row< - [0..maxRow-1]
return(binomialRow row)
where
binomialRow row =
do k < - [0..row ]
return(二项式行k)

printTriangle :: Int - > IO()
printTriangle rows = do row <-( triangle)
putStrLn(show row)
where
triangle = pascalTriangle rows

现在由于对训练有素的眼睛可能是明显的原因,但对我来说完全笼罩在神秘面纱中,当我尝试以ghci加载时出现以下错误:

 无法与实际类型为[[Int]]的预期类型'IO t0'匹配
In 'do'表达式的一行:row< - (triangle)
在表达式中:
do {row< - (triangle);
putStrLn(show row)}
In
'printTriangle'的等式:
printTriangle rows
= do {row< - (triangle);
putStrLn(show row)}
where
triangle = pascalTriangle rows



<

  printTriangle 3 
我想要做的事情就像我打电话给printTriangle一样。 $ c>

我得到这个输出:

  [1] 
[1,1]
[1,2,1]



如果有人能向我解释为什么我在这里做的事情不起作用(说实话,我不太确定我在这里做什么;我习惯于命令式语言,而这整个函数式编程的东西是仍然有可能让我感到困惑),以及如何以一种不那么愚蠢的方式做到这一点,那将是非常棒的。



预先致谢。

解决方案

你在评论中说过,你认为列表是单子,但现在你不确定 - 呃,你是对的,列表 monads!那么为什么你的代码不工作?



好吧,因为 IO 也是一个monad。所以当编译器看到 printTriangle :: Int - > IO(),然后做记号,它说啊哈,我知道该怎么做!他正在使用IO monad!尝试想像它的震撼和失望,当它发现,而不是IO单子,它发现列表monad内!



因此,这是问题:打印,并处理外世界,你需要使用IO monad;在函数内部,你试图使用列表作为monad。



让我们看看这是一个问题。 do-notation是Haskell的语法糖引诱我们进入它的蛋糕屋并且吃我们....我的意思是它是>> = (发音绑定)的语法糖引诱我们使用monads(并享受它)。所以让我们使用bind来写 printTriangle

  printTriangle rows =(pascalTriangle rows )>> =(\row  - > 
putStrLn $ show row)

好吧,那很简单。现在我们看到有什么问题吗?那么,让我们看看类型。什么是绑定类型? Hoogle说:(>> =):: Monad m => m a - > (a - > m b) - > m b 。好的,谢谢Hoogle。所以基本上,绑定需要一个monad类型包装一个类型的个性,一个函数将类型转换为包含type-b个性的(相同的)monad类型,并且以相同的monad类型包装类型 - b性格。



因此,在我们的 printTriangle 中,我们有什么?




  • pascalTriangle rows :: [[Int]] - 所以我们的monad是 [] ,个性为 [Int]

  • (\row - > ; putStrLn $ show row):: [Int] - > IO() - 这里monad是IO,个性是()



好吧,废话。 Hoogle非常清楚地告诉我们,我们必须匹配我们的monad类型,相反,我们已经给出了>> = 列表单子,以及一个函数产生一个IO monad。这使得Haskell表现得像一个小孩子:它闭上眼睛,在地板上跺脚尖叫着不!不!不!甚至不会看你的程序,更不用说编译它。






那么我们如何安抚Haskell?那么其他人已经提到 mapM _ 。并且向顶层函数添加明确的类型签名也是一个好主意 - 它有时可以帮助您更快地获取编译错误,而不是稍后(并且获得它们;毕竟这是Haskell)),这使得它理解这些错误消息要容易得多。



我会建议编写一个函数来转换你的 [[Int]] 转换为字符串,然后单独打印出字符串。通过将转换分离为IO-nastiness中的字符串,这将允许您继续学习Haskell,而不必担心 mapM _ &朋友,直到你很好,准备好。

  showTriangle :: [[Int]]  - > String 
showTriangle triangle = concatMap(\ line - > show line ++\\\
)triangle

  showTriangle = concatMap(\ line  - > show line ++\\\

然后 printTriangle

  printTriangle :: Int  - > IO()
printTriangle rows = putStrLn(showTriangle $ pascalTriangle rows)

  printTriangle = putStrLn。 showTriangle。 pascalTriangle 


I suppose everyone here already has seen one of these (or at least a similar) question, still I need to ask because I couldn't find the answer to this question anywhere (mostly because I don't know what exactly I should be looking for)

I wrote this tiny script, in which printTriangle is supposed to print out the pascal triangle.

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

Now for reasons that are probably obvious to the trained eye, but completely shrouded in mystery for me, i get the following error when trying to load this in 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

what im trying to do is something like I call printTriangle like this:

printTriangle 3

and I get this output:

[1]
[1,1]
[1,2,1]

If anyone could explain to me why what I'm doing here doesn't work (to be honest, I am not TOO sure what exactly I am doing here; I am used to imperative languages and this whole functional programming thingy is still mighty confusing to me), and how I could do it in a less dumb fashion that would be great.

Thanks in advance.

解决方案

You said in a comment that you thought lists were monads, but now you're not sure -- well, you're right, lists are monads! So then why doesn't your code work?

Well, because IO is also a monad. So when the compiler sees printTriangle :: Int -> IO (), and then do-notation, it says "Aha! I know what to do! He's using the IO monad!" Try to imagine its shock and dispair when it discovers that instead of IO monads, it finds list monads inside!

So that's the problem: to print, and deal with the outside world, you need to use the IO monad; inside the function, you're trying to use lists as the monad.

Let's see how this is a problem. do-notation is Haskell's syntactic sugar to lure us into its cake house and eat us .... I mean it's syntactic sugar for >>= (pronounced bind) to lure us into using monads (and enjoying it). So let's write printTriangle using bind:

printTriangle rows = (pascalTriangle rows) >>= (\row -> 
                     putStrLn $ show row)

Okay, that was straightforward. Now do we see any problems? Well, lets look at the types. What's the type of bind? Hoogle says: (>>=) :: Monad m => m a -> (a -> m b) -> m b. Okay, thanks Hoogle. So basically, bind wants a monad type wrapping a type a personality, a function that turns a type a personality into (the same) monad type wrapping a type-b personality, and ends up with (the same) monad type wrapping a type-b personality.

So in our printTriangle, what do we have?

  • pascalTriangle rows :: [[Int]] -- so our monad is [], and the personality is [Int]
  • (\row -> putStrLn $ show row) :: [Int] -> IO () -- here the monad is IO, and the personality is ()

Well, crap. Hoogle was very clear when it told us that we had to match our monad types, and instead, we've given >>= a list monad, and a function that produces an IO monad. This makes Haskell behave like a little kid: it closes its eyes and stomps on the floor screaming "No! No! No!" and won't even look at your program, much less compile it.


So how do we appease Haskell? Well, others have already mentioned mapM_. And adding explicit type signatures to top-level functions is also a good idea -- it can sometimes help you to get compile errors sooner, rather than later (and get them you will; this is Haskell after all :) ), which makes it much much easier to understand the error messages.

I'd suggest writing a function that turns your [[Int]] into a string, and then printing the string out separately. By separating the conversion into a string from the IO-nastiness, this will allow you to get on with learning Haskell and not have to worry about mapM_ & friends until you're good and ready.

showTriangle :: [[Int]] -> String
showTriangle triangle = concatMap (\line -> show line ++ "\n") triangle

or

showTriangle = concatMap (\line -> show line ++ "\n")

Then printTriangle is a lot easier:

printTriangle :: Int -> IO ()
printTriangle rows = putStrLn (showTriangle $ pascalTriangle rows)

or

printTriangle = putStrLn . showTriangle . pascalTriangle

这篇关于Haskell初学者,试图输出一个列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆