【问题标题】:Haskell list filtering with two arguments带有两个参数的 Haskell 列表过滤
【发布时间】:2019-05-22 20:08:58
【问题描述】:

我必须编写一个函数,它使用一个结果为True 的参数进行过滤,然后使用另一个结果为False 的参数进行过滤

我试过了:

selectUnless :: (t -> Bool) -> (t -> Bool) -> [t] -> [t]
selectUnless fx gx (x:xs) = filter gx (filter fx (x:xs))

但我需要那个“不是 gx”的列表。

例如:

selectUnless (>= 2) (==2) [1,2,3,4] == [3,4]
selectUnless even odd [1..50] == [2,4..50]

【问题讨论】:

    标签: list haskell filter


    【解决方案1】:

    Haskell 有一个 not :: Bool -> Bool 运算符,可将 True 转换为 False,反之亦然。

    问题当然在于gx 不是 Bool,而是一个函数t -> Bool。然而,我们可以构造一个函数,如:

    \x -> not (gx x)
    

    因此,这会将not 应用于gx x 的结果。或者我们可以使用(.) :: (b -> c) -> (a -> b) -> a -> c,比如:

    selectUnless :: (t -> Bool) -> (t -> Bool) -> [t] -> [t]
    selectUnless fx gx = filter (not . gx) . filter fx

    我们甚至可以通过使用liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c 来实现完全无点:

    import Control.Applicative(liftA2)
    
    selectUnless :: (t -> Bool) -> (t -> Bool) -> [t] -> [t]
    selectUnless = (filter .) . (. (not .)) . liftA2 (&&)

    或者@JonPurdy提供的更优雅的解决方案:

    import Control.Applicative(liftA2)
    import Data.Function(on)
    
    pre :: (a -> b) -> (b -> c) -> a -> c
    pre = flip (.)
    
    filtered :: (([a] -> [a]) -> ([a] -> [a]) -> c) -> (a -> Bool) -> (a -> Bool) -> c
    filtered = (`on` filter)
    
    selectUnless :: (a -> Bool) -> (a -> Bool) -> [a] -> [a]
    selectUnless = pre (not .) . filtered (.)

    请注意,您的原始代码无法处理空列表:实际上您只在函数中为列表指定了(x:xs) 模式,而不是[]。不过这里没有必要使用额外的模式,filter 已经可以处理空列表和非空列表。

    【讨论】:

    • 那个无点版本有点毛茸茸-我更喜欢selectUnless = pre (not .) . filtered (.),其中pre = flip (.)filtered = (`on` filter),或者部分无点版本filter (f <&&> not . g)在答案中莱因·亨里希斯
    【解决方案2】:

    因为filter f . filter g = filter (\x -> f x && g x),我们只需要某种方式来反转g。正如 Willem 所提到的,它以 not 的形式存在。所以我们有:

    selectUnless f g = filter (\x -> f x && not (g x))
    

    如果你想更聪明一点,你可以举&&

    (<&&>) = liftA2 (&&)
    infixr 3 <&&>
    
    selectUnless f g = filter (f <&&> not . g) 
    

    您甚至可能认为这是简洁且足以揭示意图的内容,以至于不需要它自己的名字。

    【讨论】:

    • 该运算符更方便使用 Monad 约束以使其正确短路:a &lt;&amp;&amp;&gt; b = a &gt;&gt;= \case True -&gt; b; False -&gt; pure False(类似&lt;||&gt;
    • @JonPurdy As was pointed out to me 当我对自己说了同样的话时...... (-&gt;) r Applicative 已经短路了。如果您希望它与任意 Monad 短路,您只需要这样做。
    • 对,我应该澄清一下,这确实进行短路评估,而不是执行m的其他选择的影响
    猜你喜欢
    • 1970-01-01
    • 2011-10-26
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-23
    相关资源
    最近更新 更多