如何在Haskell中执行多个操作

时间:2013-12-01 23:37:13

标签: haskell

我试着写一个简单的函数来学习Haskell中的IO monad。该函数应该从控制台获取一些给定整数的总和,但是当函数运行了4次时它会显示“1 ***例外:Char.digitToInt:不是数字'\ n'”

import Data.Char

readInts :: IO ()
readInts = do
    putStrLn "Choose nr of ints to sum."
    c <- getChar
    let i = digitToInt c
    -- code.
    let sum = getInts i
    let str = "\nSum is: " ++ [intToDigit i].
    putStrLn str

getInt :: IO Int 
getInt = do
    c <- getChar
    return (digitToInt c)

getInts :: Int -> IO Int
getInts n = if n == 0 
               then return 0
               else 
                   do i <- getInt
                      j <- getInts
                      return (i+j)  

有人可以解释一下我的递归错误吗?

2 个答案:

答案 0 :(得分:2)

getChar获取每个字符,包括您键入的任何“\ n”(您需要提交答案)。尝试过滤掉这些。

解决此问题的更好方法是使用“交互”来获取用户输入,并使用行分解数据。这样用户可以输入多位数字,并且“\ n”将被删除。

以下汇总在命令行输入的数字(没有任何提示,输入^ d结束输入)。

main = interact (show . sum . fmap read . lines)

答案 1 :(得分:2)

您只是使用错误的工具在“键盘数据”和数字之间进行转换。与IO几乎没有关系。

顾名思义,

intTodigit会对单位 /字符起作用,而不是对一般数字起作用。你想要的是读/打印整个字符串,它可以处理多位数字。将getChar替换为getLine,将digitToInt替换为read,将[intToDigit i]替换为show i。然后它应该工作正常。

但是,最好进行一些简化。

  • getInt基本上已存在,但更常见的形式是:readLnstdin获取一行并将其作为必需类型进行解释。
  • getInts的实施比必要的复杂得多。对计数变量的显式递归(BTW,在递归中它必须是getInts (n-1))是丑陋的;这种循环显然是如此常见,以至于存在一个标准的解决方案(你需要import Control.Monad),它看起来很像你可能习惯的命令式语言中的循环:

    getIntsAndSum :: Int -> IO Int
    getIntsAndSum n = fmap sum . forM [1 .. n] $ \k -> do
                             i <- getInt
                             return i
    

    实际上可以进一步简化为

    fmap sum . forM [1 .. n] $ \_ -> getInt
    

    因为do阻止了一个可选的构造来帮助你动作,但是当只有一个动作时你可以自己编写它。

  • 理想情况下,您根本不需要首先要求数字:只需收集您给出的所有数字,然后总结它们。正如jamshidh所说,这非常简单,interact

    main = interact processInput
     where processInput allInput = show (sum allNumbers)
            where allNumbers = map read . lines allInput
    

    就是这样!没有别的需要。事实上,这可以写得更简单:你基本上只有一个简单的数据流管道。

    main = interact $ show . sum . map read . lines