读取行直到空字符串,然后将它们相乘
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
我有一个关于 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