繁体   English   中英

Haskell拆分列表函数无限类型错误

[英]Haskell Split List Function Infinite Type Error

我正在Haskell中使用一个函数,该函数将一个列表并将其分为两个大小均匀的列表。 这是我所拥有的:

split (x:y:xs) = split2 ([((length(x:y:xs) `div` 2)-2) : x ++ y] : [xs])
split2 (x:xs:[y:ys]) = split2 ((x-1) : [xs] ++ y : [ys])
split2 (0:xs:[y:ys]) = (xs:[y:ys])

该函数采用列表的前两个元素,并将它们放到列表#2中,并将第一个列表附加为第二个元素。 然后,它获得列表的长度,并将其除以二,以考虑到已经从第一个列表中删除了两个元素这一事实,找出要运行多少次。 然后,它将这两条信息放入split2中,split2从第一个列表中获取另一个元素,并将其追加到第一个元素中的第二个列表中,并且从运行次数开始倒数1,然后再次运行。

问题是,当我运行它时,我得到以下信息:

Functions.hs:19:49:
    Occurs check: cannot construct the infinite type: t0 = [t0]
    In the first argument of `(:)', namely `(y)'

19代表第2行,第一个split2函数。 不确定如何解决此错误。 有任何想法吗?

很难知道从哪里开始...

让我们从split2更大的表达式块中定义函数。

f1 (x:y:xs) = (length(x:y:xs) `div` 2)-2
f1 :: [a] -> Int

好的,所以参数是一个列表,它返回一个Int

f2 (x:y:xs) = ((length(x:y:xs) `div` 2)-2) : x
f2 :: [[Int]] -> [Int]

在这里, length Intx ,因此x必须为[Int] ,因此(x:y:xs)必须为[[Int]] 我们还可以推断出yx具有相同的类型,并且xs是相同类型的事物的列表; [[Int]] 因此x ++ y也将是[Int]

因此, [xs]将具有[[[Int]]] 现在,我们将结果包装在列表构造函数中,并使用[xs]

f3 (x:y:xs) = [((length(x:y:xs) `div` 2)-2) : x ++ y] : [xs]
f3 :: [[Int]] -> [[[Int]]]

我猜您不希望参数是Int列表的列表。

现在,如果我们查看split2 ,则参数模式(x:xs:[y:ys])表示其类型为:

split2 :: [[a]] -> b
     x :: [a]
    xs :: [a]
     y ::  a
    ys :: [a]

split2的第一个定义的split2试图通过串联(x-1) : [xs]y : [ys]来构造一个新列表。 但是,如果将类型替换为y : [ys] ,则会发现:

y : [ys] :: a : [[a]]

但是由于(:) :: a -> [a] -> [a] ,这意味着[[a]]必须与[a]相同,或者a必须是其自身的列表,这是不可能的。

(x-1)类型也不正确,因为它试图从列表中减去一个。

我无法告诉您是否要将列表分为偶数和奇数元素,或分为前半部分和后半部分。

这是两个分为上半部分和下半部分的版本,如果长度为奇数,则向下舍入(RD)或向上舍入(RU):

splitRD xs = splitAt (length xs `div` 2) xs
splitRU xs = splitAt ((length xs + 1) `div` 2) xs

这是将列表分为偶数和奇数元素的版本:

splitEO [] = ([], [])
splitEO [e] = ([e], [])
splitEO (e:o:xs) = (e:es, o:os) where (es, os) = splitEO xs

很少的建议

  • 将类型写入所有功能。 它使代码更具可读性,还有助于捕获错误。

  • ++的类型是[a] -> [a] -> [a] ,您要添加列表的长度以及元素。 由于list必须是统一类型,并且长度返回Int类型,因此编译器将split类型推断为[[Int]] -> t (假设split2返回类型t )。

  • 当您传递([((length(x:y:xs) div 2)-2) : x ++ y] : [xs])到split2。 xs[[Int]]类型,这意味着[xs][[[Int]]]类型,因此编译器将split2类型推断为[[[Int]]] -> t

现在在split2的定义中

 split2 (x:xs:[y:ys]) = split2 ((x-1) : [xs] ++ y : [ys])

ys[[Int]]类型,所以y[Int]类型。 xs的类型为[[Int]] ,但是您正在执行[xs] ++ y ,这意味着[xs]y都应为同一类型(对于某些a[a] )。

由于您未提供任何类型,因此编译器完全困惑如何推断此类。

如果您只是想将列表分成两个相等的部分,那为什么不做更简单的事情

split3 :: [a] -> ([a], [a])
split3 [] = ([],[])
split3 [x] =  ([x],[])
split3 (x:y:xs) = let (xs',ys') = split3 xs in (x:xs',y:ys')

在Haskell中,列表的元素必须全部为同一类型。 在函数中,列表包含Ints,原始列表的元素和原始列表的子列表的混合,所有这些可能都是不同的类型。

您还对如何附加列表和元素有些困惑。 x ++ y只能在x和y本身是列表时使用,而x:y只能在y是列表并且x是列表的元素时使用; 要创建一个包含x和y作为元素的新列表,请改用[x,y](尽管x:[y]也可以)。 同样,[xs] ++ y需要改为xs ++ [y]。

在不更改基本算法的情况下,最简单的解决方案可能是让split2接受3个单独的参数。

split (x:y:xs) = split2 ((length(x:y:xs) `div` 2)-2) [x,y] xs
split2 n xs (y:ys) = split2 (n-1) (xs++[y]) ys
split2 0 xs ys = [xs,ys]

您似乎在列表中传递状态,而不是将值作为函数传递给函数,这在编译器看来好像在创建异构值列表时会产生问题,而Haskell中的列表应该是同质类型。

代替

split2 (0:xs:[y:ys])

您应该像这样将不同的参数/值分别传递给函数

split2 n xs (y:ys)

您正在寻找的功能也会在标准库函数中复制。

halveList xs = splitAt (length xs `div` 2) xs

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM