我试着写一个简单的函数来学习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)
有人可以解释一下我的递归错误吗?
答案 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
基本上已存在,但更常见的形式是:readLn
从stdin
获取一行并将其作为必需类型进行解释。 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