Haskell - 过滤最后一个元素

时间:2015-08-06 20:13:56

标签: haskell

我想过滤不满足属性的列表的最后一个元素。一个例子是

smallerOne :: a->Bool 
smallerOne x = x < 1 

函数filterLast应该给出

filterLast smallerOne [1, -2, -3, -4, 5] 
[1, -2, -3, -4] //Filters the last element that is not smaller than 1 

这是我的代码(我是初学者,对于不好的尝试感到抱歉)

filterLast :: (a -> Bool) -> [a] -> [a] 
filterLast p [] = [] 
filterLast p xs 
    | p (last xs) = filterLast p (init xs) : last xs 
    | otherwise = init xs 

感谢您的帮助

4 个答案:

答案 0 :(得分:6)

导致filterLast编译的最小变化是使用(++)而不是(:),如:

filterLast p xs 
    | p (last xs) = filterLast p (init xs) ++ [last xs]

(其他行保持不变。)(:)函数专门用于在列表的开头添加一个额外元素,这不是您想要在此处执行的操作。对于smallerOne,您可以按照错误消息建议的方式更改类型签名,因此:

smallerOne :: (Num a, Ord a) => a->Bool

答案 1 :(得分:4)

以下是if (array[i] > biggestInt)的一种可能实现方式:

filterLast

我们的想法是重复使用filterLast :: (a -> Bool) -> [a] -> [a] filterLast p = go [] where go chunk xs = case span p xs of (left, []) -> left (left, (r:right)) -> chunk ++ left ++ go [r] right 将序列分成两部分:左半部分都满足谓词,然后右半部分从第一个不满足谓词的元素开始。如果没有右半边,那么我们可以直接返回左半边。否则,我们必须:

  1. 将右半部分的第一个元素保存为&#34;候选人&#34;只有在我们能够找到不满足谓词
  2. 的后续元素时才会包含的项目
  3. 在结果中包含上一个候选元素(如果有)。
  4. 对于大型列表(尤其是无限列表!)而言,这比使用spanlast的问题时使用的方法更有效。但如果这对你来说不是一个重要的问题,那么简单地应用Daniel Wagner的回答中提出的修正将会为你提供一个你会发现更容易理解的功能。

    编辑:正如评论中所建议的那样,您可以通过将此函数重命名为init然后定义一个角来修复一个角落案例(所有项目都满足谓词的无限列表)委托给它的新filterLast'

    filterLast

    请注意,仍有一些序列在不产生输出的情况下发生分歧,例如filterLast :: (a -> Bool) -> [a] -> [a] filterLast p xs = left ++ filterLast' p right where (left, right) = span p xs 。但我认为任何实现都无法解决这个问题,因为你永远不知道是否在输出列表中包含filterLast (< 1) $ 10 : repeat -1,因为你永远不会找到大于1的另一个元素。

答案 2 :(得分:-1)

你想要做的是颠倒列表并在他们不满足财产时采取元素。元素满足属性的那一刻,你放弃它并占据列表的其余部分。这很自然地转换为haskell代码。

filterLast :: (a -> Bool) -> [a] -> [a]
filterLast p = reverse . uncurry (++) . dropHeadSnd . span p . reverse
  where
    dropHeadSnd (x, y) = (x, tail' y)
    tail' [] = []
    tail' (x:xs) = xs

(++)降低了代码的效率,虽然它不是渐近效率,但消除(++)会提高你的性能。

filterLast :: (a -> Bool) -> [a] -> [a]
filterLast p = reverse . helper . reverse
  where
    helper [] = [] 
    helper (x:xs) | p x = xs
                  | otherwise = x:helper xs

上述两个函数都在列表上使用两次迭代。但是请记住递归,你可以从后面和前面获取信息。我们使用递归调用来确定是否存在满足'p'的任何后续元素。

f :: (a -> Bool) -> [a] -> [a]
f p = snd . helper 
 where
   helper [] = (False, [])
   helper (a:as) | p a && flag = (flag, a:as')
                 | p a = (True, as')
                 | otherwise = (flag, a:as')
          where
           (flag, as') = helper as

答案 3 :(得分:-4)

smallerOne :: (Ord a, Num a) => a -> Bool 
smallerOne x = x < 1 

filterLast :: (a -> Bool) -> [a] -> [a]
filterLast p (x:[]) = if (p x) then x:[] else []
filterLast p (x:xs) = x : filterLast p xs

这是您问题的解决方案。现在,还有一些解释:

  

smallerOne ::(Ord a,Num a)=&gt; a - &gt;布尔

您必须包含类约束OrdNum,因为您正试图通过此<比较运算符来设置数字。您可以在此处阅读更多内容:http://learnyouahaskell.com/types-and-typeclasses#typeclasses-101

filterLast是通过pattern matching实现的,Haskell非常聪明,将进入与给定列表匹配的分支,因此他将进入此分支:

  

filterLast p(x:[])= if(p x)then x:[] else []

仅当剩下一个元素时,即last element

你说你是初学者,我真的推荐你这个教程,http://learnyouahaskell.com/chapters,这很酷。