我正在研究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中,并将第一个列表作为第二个元素追加。然后它获取列表的长度,并将其除以2,以确定运行了多少次,考虑到它已经从第一个列表中删除了两个元素。然后它接收这两个信息并将其放入split2,它从第一个列表中获取另一个元素并将其附加到第一个元素中的第二个列表中,它也从运行次数向下计数1,然后再次运行。 / p>
问题是,当我运行它时,我得到了这个:
Functions.hs:19:49:
Occurs check: cannot construct the infinite type: t0 = [t0]
In the first argument of `(:)', namely `(y)'
19指的是第2行,第一个split2函数。不完全确定如何修复此错误。有什么想法吗?
答案 0 :(得分:3)
很难知道从哪里开始...
让我们从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
Int
与x
一致,因此x
必须为[Int]
,因此(x:y:xs)
必须为[[Int]]
。我们还可以推断y
与x
的类型相同,而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
s列表的列表。
现在,如果我们查看split2
,参数模式(x:xs:[y:ys])
意味着它的类型为:
split2 :: [[a]] -> b
x :: [a]
xs :: [a]
y :: a
ys :: [a]
split2
的第一个定义的rhs尝试通过连接(x-1) : [xs]
和y : [ys]
来构建新列表。但是,如果我们将类型替换为y : [ys]
,我们会发现:
y : [ys] :: a : [[a]]
但是从(:) :: a -> [a] -> [a]
开始,这意味着[[a]]
必须与[a]
的类型相同,或a
必须是自己的列表,这是不可能的。< / p>
(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
答案 1 :(得分:0)
很少有建议
将类型写入所有功能。它使代码更具可读性,并有助于捕获错误。
++
的类型为[a] -> [a] -> [a]
,您要添加列表的长度以及元素。由于list必须是统一类型,而length返回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')
答案 2 :(得分:0)
在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]
答案 3 :(得分:0)
你好像是在列表中传递状态而不是作为函数的值传递,这在编译器看起来好像你正在创建一个异构值列表时会产生问题,而Haskell中的列表应该是同质型。
而不是
split2 (0:xs:[y:ys])
你应该像这个
分别将不同的参数/值传递给函数split2 n xs (y:ys)
您正在寻找的功能也会在标准库函数中重现。
halveList xs = splitAt (length xs `div` 2) xs