了解递归函数列表中的列表-Haskell

时间:2018-11-01 19:19:00

标签: haskell

我正在编写一个以元组或三元组排序给定列表的函数。它应该按列表两个元素之间的数字排序。

例如,如果给定列表为[1,2,3,6,7],则应返回[[1,2,3], [6,7]] 因为在 1,2 2,3 之间以及在 6,7

之间存在零数字

这是我的代码

import Data.List 

check :: [Int] -> [[Int]]
check listCopy@(x:xs) = 
    let sorted = sort (listCopy) 
        in if (length sorted > 1) 
              then if ((sorted !! 1 ) - (sorted !! 0)) == 1 || ((sorted !! 1 ) - (sorted !! 0)) == 0 
                      then [[x]  ++ check(xs) !! 0] 
                      else [[x]] ++ check(xs)
              else [[x]]
check [] = [[]]

if ((sorted !! 1 ) - (sorted !! 0)) == 1 || ((sorted !! 1 ) - (sorted !! 0)) == 0正在检查列表的两个元素之间是否有0个数字

如果以上语句为True,则[[x] ++ check(xs) !! 0]将把该元素添加到列表中,并再次调用该函数,并采用它返回的第一个元素。例如:[1,2,3,6,7] -> [[1 ++ check [2,3,6,7]]] -> [[1,2 ++ check[3,6,7]]]等等...

但是,if ((sorted !! 1 ) - (sorted !! 0)) == 1 || ((sorted !! 1 ) - (sorted !! 0)) == 0为False时,else [[x]] ++ check(xs)应该在列表内的列表内设置元素,然后再次调用函数,并在列表内创建另一个新列表。示例:[[1,2 ++ check[3,6,7]]]->是6-3 == 0或1(False)返回[[1,2,3]] + check[6,7],其结果应为[[1,2,3],[6,7]]

呼叫check[1,2,3,6,7]会返回[[1,2,3]]。我没有错, 但是据我所知,[[1,2]] ++ [[3,4]]应该会得到[[1,2], [3,4]],而这正是我在else [[x]] ++ check(xs)中所做的事情,我的功能到此结束。我在哪里犯了错误,或者我做错了什么?

2 个答案:

答案 0 :(得分:5)

这里的问题是,您仅追加了第一个子列表

then [[x]  ++ check(xs) !! 0]

因此,您进行了递归调用,将返回一个子列表列表,但是您“丢弃”了除第一个列表之外的所有列表,然后将第一个列表串联起来。其余子列表将被忽略。

您可以使用以下方法解决此问题:

then [[x]  ++ check(xs) !! 0] ++ safeTail check

我们将safeTail实现为:

safeTail :: [a] -> [a]
safeTail (x:xs) = xs
safeTail [] = []

或者,例如@melpomene says

safeTail :: [a] -> [a]
safeTail = drop 1

后来发现我们只能使用tail,但是使用上面的代码,很难看到。

但是实现不是很“ Haskellish”。您的代码使用了许多(!!)length。由于(!!) O(k)中与 k 一起运行,因此我们想要获得元素的索引,而length中运行> O(n)且列表的长度为 n ,这也将非常无效。

在对列表进行进一步处理之前先对其进行排序是有意义的。接下来,我们只需要查找当前元素x,下一个元素n和其余元素xs,因此:

go :: (Ord n, Num n) => [n] -> [[n]]
go (x:n:xs) = ...
go other = other

在情况n <= x+1中,我们知道两个数字之间的差为零或一个,因此在这种情况下,递归检查的 head (第一个元素)应该以{{1​​}}开头,因此我们可以这样写:

x

否则,我们可以只构建一个单例列表,然后再构建其余列表:

go :: (Ord n, Num n) => [n] -> [[n]]
go (x:n:xs) | n <= x+1 = (x:r) : rs
            | otherwise = ...
   where (r:rs) = go (n:xs)
go [x] = [[x]]
go [] = []

我们知道go :: (Ord n, Num n) => [n] -> [[n]] go (x:n:xs) | n <= x+1 = (x:r) : rs | otherwise = [x]:(r:rs) where (r:rs) = go (n:xs) go [x] = [[x]] go [] = [] 至少有一个元素,因为我们用一个元素递归调用列表,并且在列表为非空的所有情况下,我们都返回一个非空的列表。

通过使用 as-pattern ,我们可以使其更加优雅:

go (n:xs)

我们可以将上述内容概括为@chepner says,使其仅需要go :: (Ord n, Num n) => [n] -> [[n]] go (x:na@(n:xs)) | n <= x+1 = (x:r) : rs | otherwise = [x]: ra where ra@(~(r:rs)) = go na go [x] = [[x]] go [] = [] Eq a

Ord a

所以现在我们只需要用go :: (Ord n, Enum n) => [n] -> [[n]] go (x:na@(n:xs)) | succ x >= n = (x:r) : rs | otherwise = [x]: ra where ra@(~(r:rs)) = go na go [x] = [[x]] go [] = [] 来表达check,就可以:

go

或者我们可以让import Data.List(sort) check :: (Ord n, Enum n) => [n] -> [[n]] check = go . sort where go (x:na@(n:xs)) | succ x >= n = (x:r) : rs | otherwise = [x]: ra where ra@(~(r:rs)) = go na go [x] = [[x]] go [] = [] 函数对check类型进行操作:

(Eq n, Enum n)

答案 1 :(得分:2)

这是一个有趣的问题,这是一种应用风格的实验方法。是的,它看起来有些令人费解,但实际上非常简单。我唯一不喜欢的是last函数的用法。也许我们可以找到一种方法以某种方式删除它。

splitConsequtives :: Integral a => [a] -> [[a]]
splitConsequtives xs = foldr id [[last xs]] $ zipWith f <*> tail $ xs
                       where f x y | y-x == 1  = (:) <$> (x:) . head <*> tail
                                   | otherwise = ([x]:)

*Main> splitConsequtives [1,2,3,6,7]
[[1,2,3],[6,7]]
*Main> splitConsequtives [-1,2,3,6,8,9]
[[-1],[2,3],[6],[8,9]]

这个想法是将构造函数放在一个列表中,这些列表在通过折叠链接时最终将构成我们的整个结果列表。列表构造函数(:)是正确的关联函数,这就是为什么我使用foldr

这一切都始于zipWith,通过xs函数(如tail)将f放入zipWith f xs (tail xs)列表中。 f :: a -> a -> [[a]] -> [[a]]函数是我们将应用程序用作结果列表元素的位置。

好..!现在,我们来关注f函数,该函数以xy作为参数。

  • 如果yx之后,则我们的[[a]] -> [[a]]类型的函数 是(:) <$> (x:) . head <*> tail,其中包含以下列表的列表: 数字然后将子列表放在head,将x附加到该子列表, 将所有内容放回原处。因此,如果我们的结果正在构建 (foldr的累加器参数)为[[7], [9,10]]x6,我们将得到[[6,7],[9,10]]
  • 如果y不是x的后继,则我们的[[a]] -> [[a]]类型 函数是([x]:)。因此,如果我们的结果正在构建( foldr的累加器参数为[[6,7], [9,10]]x3 那么我们将得到[[3],[6,7],[9,10]]

需要注意的一个有趣点是我们用于foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b的迭代函数的类型。您期望它的类型为a -> b -> b,但是id :: a -> a看起来有所不同。这怎么可能..?我相信是因为a ~ [[a]] -> [[a]]的{​​{1}}中的b ~ [[a]](a -> b -> b)

相关问题