【问题标题】:Reading lines until empty string, then multiplying them读取行直到空字符串,然后将它们相乘
【发布时间】:2015-11-17 11:09:57
【问题描述】:

我有一个关于 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

【问题讨论】:

    标签: haskell io monads


    【解决方案1】:

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

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

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

    这样做的一种方法是:

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

    大致意思是“调用getContents,然后将lines 应用于结果”。 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
    

    【讨论】:

    • 很好,谢谢!这似乎是处理这些事情的不同方法,我会记住所有这些
    【解决方案2】:

    你的第一个问题是你只需要说... 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,您可以使其成为单子。

    【讨论】:

    • 非常感谢!我已经在原始帖子中包含了最终代码:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    相关资源
    最近更新 更多