读取行直到空字符串,然后将它们相乘

Reading lines until empty string, then multiplying them

我有一个关于 Haskell IO 的小问题。我在 haskell 编程已经有一段时间了,但看在我的份上,我似乎无法全神贯注于 I/O。

赋值很简单——从标准输入中读取整数并将它们相乘。到目前为止,这是我得到的:

mulnum n = do a <- getLine
           if a == "" then n else mulnum (n * (read a :: Int))

mulInput :: IO ()
mulInput = print (mulnum 1)

错误:

Couldn't match expected type `IO b' with actual type `Int'
Relevant bindings include
  n :: IO b (bound at dayx.hs:8:8)
  mulnum :: IO b -> IO b (bound at dayx.hs:8:1)
In the second argument of `(*)', namely `(read a :: Int)'
In the first argument of `mulnum', namely `(n * (read a :: Int))'
In the expression: mulnum (n * (read a :: Int))

我强烈怀疑我从错误的角度看待它,所以如果有人至少能给我指出正确的方向,我会非常高兴。

谢谢,祝你有愉快的一天!

编辑:

非常感谢您的帮助!这是现在的样子:

mulnum :: Int -> IO Int
mulnum n = do a <- getLine
              let a1 = (read a :: Int) in if a == "" then return n else mulnum (n * a1)

mulInput :: IO ()
mulInput = mulnum 1 >>= print

你的第一个问题是你只需要说 ... then return n else ...

你的第二个问题在这里:

print (mulnum 1)

应该是:

do n <- mulnum 1
   print n

或:

mulnum 1 >>= print

解释:

由于您在 IO monad 中,if-then-else 的两个子句都必须是 IO 计算。 else 部分是因为它是对 mulnum 的递归调用,但 n 本身是一个纯值。通过写 return n 你让它成为单子的。

在Haskell中,你通常想利用惰性将不纯(I/O)和纯(计算)部分分开。

阅读所有行直到找到空行是 I/O。其他一切都是计算。

所以你想要一个函数,它可以为你提供输入行,直到你到达一个空的输入行。您可能希望将结果 return 作为字符串列表;那已经很懒了。

一种方法是这样的:

getLines :: IO [String]
getLines = lines <$> getContents

大致意思是"call getContents and then apply lines to the result"。 getContents 读取整个标准输入,但它是懒惰的。 lines 将字符串拆分为多行;再次,懒惰。

(注意:Lazy I/O 有它的问题,这导致管道库的开发,如管道和导管,但对于这个简单的例子,问题并不重要。)

现在,您只需要在第一个空行处剪切:

main = do
  lines <- getLines
  let leadingLines = takeWhile (not . null) lines

现在你有了前导线,剩下的就是解析每一行(这是一个 map)并将它们相乘(这是一个 fold,但是有专门的 product 正是这样做的)。

这里我们忽略检查数字是否正确解析。

  let p = (product $ map read leadingLines) :: Int

我添加了显式类型注释,否则 Haskell 无法决定您想要的数字类型。

终于可以打印出结果了

  print p