分裂函数 - 不能构造无限类型

时间:2014-09-14 16:19:54

标签: haskell

以下代码无法编译:

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,但我没有看到我的错误。

1 个答案:

答案 0 :(得分:3)

发布的代码定义split'在一个案例中返回acc,在另一个案例中返回acc : <<something>>。编译器消息指出,如果a0acc的类型,因为两种情况都必须返回相同的类型,它应该是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函数添加了一个类型签名,以记录其代码。