Haskell:异常&lt; <loop>&gt;在递归数据输入上

时间:2017-01-08 01:43:10

标签: haskell recursion

所以我正在尝试创建一个可以接收实验期间捕获的数据的小程序,并且在大多数情况下我认为我已经想出如何递归地接收数据,直到用户发出信号为止,然而,在终止数据时,使用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将有适当的错误处理。)

1 个答案:

答案 0 :(得分:4)

<<loop>>基本上意味着GHC检测到由一个值立即引起的无限循环(参见this questionthis 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)三元组,则会变得更整洁,这样可以更好地表达您正在读取三个值的不变量。