【问题标题】:Haskell IO reads an integer n from the user then adds up n integersHaskell IO 从用户那里读取一个整数 n,然后将 n 个整数相加
【发布时间】:2014-03-15 20:44:52
【问题描述】:

我正在尝试编写一个程序,它从用户那里读取一个整数 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

【问题讨论】:

    标签: haskell recursion io


    【解决方案1】:

    你把addNumbers当作一个普通函数处理,但它是一个IO操作,所以我们只能在doanswer &lt;- 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 =&gt; [m a] -&gt; m [a] 获取操作列表并运行它们,返回结果列表。如果我们只是列出一个充满getInts、[getInt| _&lt;-[1..num]] 或更简洁的replicate num getInt 的列表,我们可以做numbers &lt;- sequence (replicate num getInt)。在Control.Monad 中有一个简写形式,称为replicateM :: Monad m =&gt; Int -&gt; m a -&gt; 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
    

    【讨论】:

    • 谢谢,这是一个非常巧妙的方法!
    • @benharris 现在有一个更简洁的方法。
    • @AndrewC 比我快一分钟... Arrrgh!!
    • @AndrewC 完全没有不好的感觉....如果我知道你要这样做,我可以节省一些时间。
    【解决方案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
    

    【讨论】:

    • 这当然是一个非常好的方法。我完全同意这个好答案!
    【解决方案3】:

    以下基于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
    

    【讨论】:

    • 我正在尝试理解您的代码,但看不到输入字符串何时转换为可以相加的数字。
    • @epsilonhalbe readLn 进行转换。相当于fmap read getLine
    猜你喜欢
    • 1970-01-01
    • 2021-03-24
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 2016-04-19
    相关资源
    最近更新 更多