簡體   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