Haskell IO从用户读取整数n,然后将n个整数相加

时间:2014-02-16 16:37:06

标签: haskell recursion io

我正在尝试编写一个从用户读取整数n的程序,然后读取n个整数(在不同的行上),最后显示读取的n个数字的总和。

到目前为止,这是我的代码:

addNumbers :: IO ()
addNumbers = do
    putStrLn "Enter a number:"
    num <- getInt
    addNumbers2 num

addNumbers2 :: Int -> IO ()
addNumbers2 num = do
    putStrLn "Enter a number:"
    n <- getInt
    if num == 1 then
        print n
    else do
        print (n + addNumbers2 (num - 1))

目前它没有编译,错误说:

Couldn't match expected type `Int' with actual type `IO ()'
In the return type of a call of `addNumbers2'
In the second argument of `(+)', namely `addNumbers2 (num - 1)'
In the first argument of `print', namely
  `(n + addNumbers2 (num - 1))'

IO让我感到困惑,我正在努力获得输出:

Enter a number:
3
Enter a number:
2
Enter a number:
1
Enter a number:
5
Sum is: 8

3 个答案:

答案 0 :(得分:5)

您将addNumbers视为普通函数,但它是一个IO操作,因此我们只能在doanswer <- addNumbers2内获取数字,但也可以它不会返回任何东西的那一刻,它只是打印它。

我重构了一点:

addNumbers :: IO ()
addNumbers = do
    putStrLn "Enter how many numbers:" -- clearer
    num <- getInt
    sum <- addNumbers2 num  -- use new version to return sum
    print sum               -- print them here

现在addNumbers2实际上添加了它们并返回它们:

addNumbers2 :: Int -> IO Int
addNumbers2 num = do
    putStrLn "Enter a number:"
    n <- getInt
    if num == 1 then
        return n      -- pass the number back
    else do
        therest <- addNumbers2 (num - 1) -- get the rest of them
        return (n + therest)             -- add them up

有效:

addNumbers
Enter how many numbers:
3
Enter a number:
1
Enter a number:
2
Enter a number:
3
6

更好的方法

sequence :: Monad m => [m a] -> m [a]获取操作列表并运行它们,返回结果列表。如果我们只列出getInt[getInt| _<-[1..num]]或更多,replicate num getInt,我们可以numbers <- sequence (replicate num getInt)。在Control.Monad中有一个简写,称为replicateM :: Monad m => Int -> m a -> m [a]

尽管如此,这样做会更好:

import Control.Monad

addNumbers' = do
    putStrLn "Enter how many numbers:" 
    num <- getInt
    numbers <- replicateM num (putStrLn "Enter a number" >> getInt)
    print (sum numbers)

给出了

Enter how many numbers:
3
Enter a number
10
Enter a number
20
Enter a number
30
60

答案 1 :(得分:2)

您可以(并且应该)使用可用的组合器

addNumbers2 n = do
    n_numbers <- replicateM n (putStrLn "Number, please: " >> getInt)
    let result = sum n_numbers
    return result

关键的洞察力是结合IO行动

 putStrLn "string" :: IO ()
 getInt            :: IO Int

 (putStrLn "Number?" >> getInt) :: IO Int

所以我们有一个IO动作,要求输入并读取它。

现在,我们可以使用

replicateM :: Int -> IO a -> IO [a]

由于我们传递了返回Int的IO操作,因此我们会返回Int的列表。

numbers <- replicateM n (putStrLn "Number?" >> getInt)

运行给定的IO动作n次并收集结果。 剩下的就是将数字相加并在IO Monad中返回它们。 或者,如果您只想打印总和,也可以

replicateM n (putStrLn "Number?" >> getInt) >>= putStrLn . show . sum

答案 2 :(得分:2)

以下基于pipes的解决方案与已接受的解决方案相比具有一个小优势,即它不会在大量行上堆叠溢出:

import Pipes
import qualified Pipes.Prelude as Pipes

main = do
    numLines <- readLn
    total    <- Pipes.sum (Pipes.replicateM numLines readLn)
    print total

使用示例:

$ ./example
3<Enter>
10<Enter>
20<Enter>
30<Enter>
60