所以我正在尝试创建一个可以接收实验期间捕获的数据的小程序,并且在大多数情况下我认为我已经想出如何递归地接收数据,直到用户发出信号为止,然而,在终止数据时,使用haskell抛出Exception: <<loop>>
并且我无法弄清楚原因。这是代码:
readData :: (Num a, Read a) => [Point a] -> IO [Point a]
readData l = do putStr "Enter Point (x,y,<e>) or (d)one: "
entered <- getLine
if (entered == "d" || entered == "done")
then return l
else do let l = addPoint l entered
nl <- readData l
return nl
addPoint :: (Num a, Read a) => [Point a] -> String -> [Point a]
addPoint l s = l ++ [Point (dataList !! 0) (dataList !! 1) (dataList !! 2)]
where dataList = (map read $ checkInputData . splitOn "," $ s) :: (Read a) => [a]
checkInputData :: [String] -> [String]
checkInputData xs
| length xs < 2 = ["0","0","0"]
| length xs < 3 = (xs ++ ["0"])
| length xs == 3 = xs
| length xs > 3 = ["0","0","0"]
据我所知,异常表明某处存在无限循环,但我无法弄清楚为什么会发生这种情况。据我所知,当输入“完成”时,当前级别应该简单地返回l,它给出的列表,然后应该级联函数的先前迭代。
感谢您的帮助。 (是的,一旦我弄清楚如何做到这一点,checkInputData将有适当的错误处理。)
答案 0 :(得分:4)
<<loop>>
基本上意味着GHC检测到由一个值立即引起的无限循环(参见this question或this one,如果您感到好奇,可以获得更多技术细节)。在这种情况下,由以下方式触发:
else do let l = addPoint l entered
这个定义会影响您作为参数传递的l
,它根据自身定义l
。你打算写点像......
else do let l' = addPoint l entered
...根据原始l'
定义新值l
。
正如Carl指出的那样,打开-Wall
(例如,通过命令行将其传递给GHC,或者使用GHCi中的:set -Wall
)会让GHC警告你关于阴影:
<interactive>:171:33: warning: [-Wname-shadowing]
This binding for ‘l’ shadows the existing binding
bound at <interactive>:167:10
此外,正如dfeuer强调的那样,else
分支中的整个阻止可以替换为:
readData (addPoint l entered)
作为一个不相关的建议,在这种情况下,最好用模式匹配替换length
和(!!)
的用法。例如,checkInputData
可以写成:
checkInputData :: [String] -> [String]
checkInputData xs = case xs of
[_,_] -> xs ++ ["0"]
[_,_,_] -> xs
_ -> ["0","0","0"]
而 addPoint
可能会变成:
addPoint :: (Num a, Read a) => [Point a] -> String -> [Point a]
addPoint l s = l ++ [Point x y z]
where [x,y,z] = (map read $ checkInputData . splitOn "," $ s) :: (Read a) => [a]
如果您更改checkInputData
以使其返回(String, String, String)
三元组,则会变得更整洁,这样可以更好地表达您正在读取三个值的不变量。