以下代码无法编译:
split :: Char -> String -> [String]
split x ys = split' x ys []
where split' _ [] acc = acc
split' x (z:zs) acc
| x == z = acc : split' x zs []
| otherwise = split' x zs (z:acc)
编译时错误:
[1 of 1] Compiling Main ( Split.hs, interpreted )
Split.hs:5:36:
Occurs check: cannot construct the infinite type: a0 = [a0]
In the first argument of `(:)', namely `acc'
In the expression: acc : split' x zs []
In an equation for split':
split' x (z : zs) acc
| x == z = acc : split' x zs []
| otherwise = split' x zs (z : acc)
Failed, modules loaded: none.
我仔细阅读了这篇有用的post,但我没有看到我的错误。
答案 0 :(得分:3)
发布的代码定义split'
在一个案例中返回acc
,在另一个案例中返回acc : <<something>>
。编译器消息指出,如果a0
是acc
的类型,因为两种情况都必须返回相同的类型,它应该是a0 = [a0]
。由于没有类型可以满足此等式(例如String
不是[String]
),编译器会抱怨类型错误。
然后,修复可以返回包含acc
两个案例的列表。那就是:
split :: Char -> String -> [String]
split x ys = split' x ys []
where split' _ [] acc = [acc] -- changed
split' x (z:zs) acc
| x == z = acc : split' x zs []
| otherwise = split' x zs (z:acc)
作为样式注释,由于x
在每次递归调用中保持相同,因此实际上不需要该参数。
split :: Char -> String -> [String]
split x ys = split' ys []
where split' [] acc = [acc]
split' (z:zs) acc
| x == z = acc : split' zs []
| otherwise = split' zs (z:acc)
代码现在是类型安全的,但它仍然包含一个错误:
> split 'a' "123a1234a"
["321","4321",""]
这是因为代码“弹出”字符串中的字符并在acc
中“推送”它们,因此它们最终被反转。修复很简单:
split :: Char -> String -> [String]
split x ys = split' ys []
where split' :: String -> String -> [String]
split' [] acc = [reverse acc]
split' (z:zs) acc
| x == z = reverse acc : split' zs []
| otherwise = split' zs (z:acc)
在最后一个版本中,我还为worker函数添加了一个类型签名,以记录其代码。