使用高阶函数过滤第一个/最后一个

时间:2019-05-04 13:33:05

标签: haskell

我有一个filterFirst函数,该函数在某些时候只能以正确的方式工作,我不确定为什么。该函数应接收一个布尔参数,如果为false,则应删除列表中的第一个元素。如果不是这样,则应将其保留下来。

这是我一直在使用的两个条件函数:

isNegative :: Int -> Bool
isNegative x
  | x < 0 = True
  | otherwise = False

isPositive:: Int -> Bool
isPositive x
  | x > 0 = True
  | otherwise = False

功能如下:

filterFirst :: (a -> Bool) -> [a] -> [a]
filterFirst x xs = foldr condition (\x -> []) xs True
   where
   condition y ys True
      | x y = y : ys True
      | otherwise = ys False
   condition y ys False = y : ys False

filterFirst使用以下命令返回正确答案:

filterFirst isNegative [1,2,(-3)]
[2,-3]

但是这实际上过滤掉了负面因素:

filterFirst isPositive [1,2,(-3)]
[1,2]

为什么要从列表中删除负数而不是第一个元素?

而且,这可能需要成为辅助职位,但是有一种方法可以对此稍作更改,以便在满足条件的情况下过滤最后一个元素?

1 个答案:

答案 0 :(得分:0)

您编写的函数实际上并不会删除列表的第一个元素,而是会删除与谓词不匹配的第一个元素

救援的等式推理!

让我们展开foldr应用程序:

foldr f a [x1, x2, ... xn] = f x1 (f x2 (... (f xn a)))

因此适用于您的示例:

foldr condition (\x -> []) [1, 2, (-3)] True
    = condition 1 ( condition 2 ( condition (-3) (\x -> []) ) ) True

因此,您可以看到foldr,顾名思义,是从右到左应用condition。 但是需要注意的另一件事是:最后一个参数True传递给condition first 应用程序,而不是最后一个:

condition 1 (...) True

现在让我们看看condition的用途。由于isPositive 1 == Truecondition的第一个子分支开始发挥作用:

| x y = y : ys True

因此上面的表达式变为:

1 : (...) True

如果我们现在带回原本折叠的内容:

1 : (condition 2 (condition (-3) (\x -> []))) True

与以下相同:

1 : (condition 2 (condition (-3) (\x -> [])) True)

通过与上述相同的逻辑,condition的第二个应用程序类似地将True参数传递给第三个应用程序:

1 : 2 : (condition (-3) (\x -> []) True)

但是第三个应用程序是不同的,因为isPositive (-3)不再是True。因此,第二个分支开始发挥作用:

| otherwise = ys False

删除(-3)并将False传递给最终参数(foldr的种子)。这使得整个表达式转换为:

1 : 2 : ((\x -> []) False)

最终等同于:

1 : 2 : []

如果此后还有更多负数,则它们不会被删除,因为最终参数现在为False,并且一直传递到最后。


现在,这里的根本问题是,对于您要解决的问题,您的解决方案过于复杂。 Haskell的一大优势在于,它可以或多或少地直接对数学语句进行编码,而无需我们都从C ++和Java中学到并喜欢的所有愚蠢的shoe角。

那么为什么不直接编码您的需求呢?

-- if it's false, it should remove the first element in the list; 
filterFirst pred (x : tail) | pred x == False = tail
-- if not, it should leave the list alone.
filterFirst _ xs = xs