Haskell:列表创建的评估列表元素

时间:2011-08-06 10:23:29

标签: list haskell

我正在尝试编写一个列表函数,该函数采用一个简单的列表并反馈一个列表列表,所有后者的元素与前一个元素具有相同的关系。

更具体地说,该功能应该这样做:

  1. 列出清单; let xs = [1,2,3,4,5,6,8,9,10]
  2. 查看头部的两个元素,如果第二个元素等于第一个元素加一个(即xs!!0 = xs!!1 - 1),则在其中创建一个列表中的列表。
  3. 该列表包含元素,而最后一个元素与从主列表中新馈送的元素具有相同的关系。当中断时,子列表关闭,但该功能应该根据相同的条件制作新的子列表。
  4. 所以,最后的结果应该是[[1,2,3,4,5,6],[8,9,10]] 缺席7将主列表分为两个子列表。它们都是算术进展,常见差异为1。
  5. 在第7章读完了解你的Haskell之后之后,我认为我真的有一个好主意,并且尝试过,无意义地失败了。欢迎帮助,欢迎!

    ghci> filter (\x y -> x + 1 == y) xs
    
    "<"interactive">":1:8:
        The lambda expression `\ x y -> x + 1 == y' has two arguments, 
        but its type `a -> Bool' has only one 
        In the first argument of `filter', namely `(\ x y -> x + 1 == y)' 
        In the expression: filter (\ x y -> x + 1 == y) xs 
        In the definition of `it': it = filter (\ x y -> x + 1 == y) xs
    

4 个答案:

答案 0 :(得分:5)

这是我对这个问题的思考过程......我们想把一个列表切成'链'(所以是一个列表列表),给出一个测试,看看是否有两个元素链接起来。

chains :: (x -> x -> Bool) -> [x] -> [[x]]

我不记得图书馆里有这样的东西,所以我决定自己动手。我想确定一个合适的递归策略来处理列表。

我能想到元素吗?否:我很快排除了mapfoldMap,因为在这个问题中,元素似乎没有彼此独立对待。

接下来,我问'输出类型是否有列表代数?'。这可能听起来不是一个明显的想法,用这种方式表达,但它解开了以下明智的问题。是否存在构建输出(链表)而不是输入(列表)的'nil'和'cons'操作?如果是这样,我可以使用foldr将输入nil-and-cons转换为输出nil-and-cons,就像这样。

chains :: (x -> x -> Bool) -> [x] -> [[x]]
chains link = foldr chCons chNil where
  -- chNil :: [[x]]
  -- chCons :: x -> [[x]] -> [[x]]

很清楚chNil必须是什么,因为我正在对原始元素进行分组。空了?空出来了!

chains :: (x -> x -> Bool) -> [x] -> [[x]]
chains link = foldr chCons [] where
  -- chCons :: x -> [[x]] -> [[x]]

我可以写chCons吗?假设我得到一个链列表:如何添加新元素?好吧,如果有一个前链我可以链接到那么我应该增长该链,否则我应该开始一个新的链。所以我在一个非空链列表的开头有一个非空链的特殊情况,默认情况下是一个单例。

chains :: (x -> x -> Bool) -> [x] -> [[x]]
chains link = foldr chCons [] where
  chCons y (xs@(x : _) : xss) | link y x  = (y : xs) : xss
  chCons y xss                            = [y] : xss

我们回家了!

> chains (\ x y -> x + 1 == y) [1,2,3,4,5,6,8,9,10]
[[1,2,3,4,5,6],[8,9,10]]

如果您可以为该类型的值实现这些运算符,那么一堆运算符对于给定类型具有代数。数据类型的构造函数只是一个代数,一组运算符的一个实现,以非常数据类型构建值。使用数据类型的输入进行计算的一种好方法是为所需类型的输出实现其代数。 foldr的目的是捕捉这个'找到代数'的模式,这就是解决这个问题的钱。

答案 1 :(得分:4)

遗憾的是,使用groupBy作为pmr建议不会起作用,因为它与每个组的第一个元素进行比较而不是比较相邻元素。这是因为groupBy假设等式谓词是可传递的。您可以使用此更改版本获得所需的行为:

groupBy rel []          =  []
groupBy rel (x:xs)      =  (x:ys) : groupBy rel zs
      where (ys,zs) = groupByAux x xs
            groupByAux x0 (x:xs) | rel x0 x = (x:ys, zs)
              where (ys,zs) = groupByAux x xs
            groupByAux y xs = ([], xs)

另请参阅:Data.List.groupBy with non-transitive equality predicate

答案 2 :(得分:3)

好的,将你的比较函数放在GHCi中,看看它给你的类型:

Prelude> :t (\x y -> x + 1 == y)
(\x y -> x + 1 == y) :: Num a => a -> a -> Bool

filter的谓词的类型为(a -> Bool);这将无法工作,因为它一次只查看一个元素。您需要类似过滤器的东西,但需要类型 (a -> a -> Bool) -> [a] -> [[a]]

Hoogle时间。 (或Hayoo,如果您愿意的话)

Hoogle与此类型签名的第一场比赛是Data.List.groupBy,这只是您正在寻找的功能。

不幸的是groupBy不太正确,正如您所发现的那样。问题是它将每个新元素与它正在测试的第一个元素进行比较,因此对于输入[1,2,3],测试函数在比较1和2时返回True,但在比较1时False然而,如果你看一下实现,可能会稍微修改一下。

groupBy                 :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _  []           =  []
groupBy eq (x:xs)       =  (x:ys) : groupBy eq zs
                           where (ys,zs) = span (eq x) xs

嗯。这使用span来划分列表,这在此处不起作用,因为span一次只查看一个参数。您可以像hammar建议的那样修改groupBy,但您也可以用其他方式编写它,例如foldr的此版本:

   myGroupBy _  []     = []
   myGroupBy eq (x:xs) = (\(_,t1,t2) -> init $ t1:t2) $ foldr (\el (el2,t1,t2) ->
     if eq el el2
       then (el,el:t1,t2)
       else (el,[el],t1:t2))
     (x,[],[]) (x:xs)

答案 3 :(得分:0)

对于您的错误:filter S类型为(a -> Bool) -> [a] -> [a]。你的lambda有两个参数,filter需要一元函数。

你想要的功能(据我所知)是groupBy :: (a -> a -> Bool) -> [a] -> [[a]]

相关问题