我有一个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]
为什么要从列表中删除负数而不是第一个元素?
而且,这可能需要成为辅助职位,但是有一种方法可以对此稍作更改,以便在满足条件的情况下过滤最后一个元素?
答案 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 == True
,condition
的第一个子分支开始发挥作用:
| 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