【问题标题】:How do you combine filter conditions如何组合过滤条件
【发布时间】:2010-10-24 22:04:08
【问题描述】:

函数的过滤器类接受一个条件(a -> Bool)并在过滤时应用它。

当您有多个条件时,使用过滤器的最佳方法是什么?

使用了applicative函数 liftA2 而不是 liftM2,因为我出于某种原因不明白 liftM2 在纯代码中是如何工作的。

【问题讨论】:

    标签: haskell filter


    【解决方案1】:

    嗯,你可以在 Haskell 中组合你想要的函数(只要类型正确),并且使用 lambdas 你甚至不必命名你的谓词函数,即,

    filter (\x -> odd x && x > 100) [1..200]
    

    【讨论】:

      【解决方案2】:

      liftM2 组合器可以在 Reader monad 中使用,以“更实用”的方式执行此操作:

      import Control.Monad
      import Control.Monad.Reader
      
      -- ....
      
      filter (liftM2 (&&) odd (> 100)) [1..200]
      

      请注意,导入很重要; Control.Monad.Reader 提供了使这一切正常工作的 Monad (e ->) 实例。

      之所以可行,是因为对于某些环境 e,reader monad 只是 (e ->)。因此,布尔谓词是在与其参数相对应的环境中返回 bool 的 0 元一元函数。然后我们可以使用 liftM2 将环境分布到两个这样的谓词上。

      或者,更简单地说,当类型成功时,liftM2 会表现得有点像这样:

      liftM2 f g h a = f (g a) (h a)
      

      如果您希望能够轻松地将它们链接起来,并且/或者不想与 liftM2 混淆,您也可以定义一个新的组合器:

      (.&&.) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
      (.&&.) f g a = (f a) && (g a)
      -- or, in points-free style:
      (.&&.) = liftM2 (&&)    
      
      filter (odd .&&. (> 5) .&&. (< 20)) [1..100]
      

      【讨论】:

      • 即使您不导入 Control.Monad.Reader,这两个示例都适用于 GHC 7.6.3。
      • 我是 liftM2(现在)可以这样替换:filter ((&amp;&amp;) &lt;$&gt; odd &lt;*&gt; (&gt;100)) [1.200]。这是相同的,但更漂亮。 :) 它也只需要Control.Applicative,并且不需要完整的单子。 …虽然我仍然想知道什么运算符允许对两个以上的布尔函数进行与运算…
      【解决方案3】:

      假设您的条件存储在名为conditions 的列表中。此列表的类型为 [a -&gt; Bool]

      要将所有条件应用于值x,您可以使用map

      map ($ x) conditions
      

      这会将每个条件应用于x 并返回一个 Bool 列表。要将此列表简化为单个布尔值,如果所有元素都为 True,则为 True,否则为 False,您可以使用 and 函数:

      and $ map ($ x) conditions
      

      现在你有了一个结合所有条件的函数。给它起个名字吧:

      combined_condition x = and $ map ($ x) conditions
      

      这个函数的类型是a -&gt; Bool,所以我们可以在调用filter时使用它:

      filter combined_condition [1..10]
      

      【讨论】:

      • 小心使用 ($x) 而不是 ($ x),就好像您出于某种其他原因打开了 Template Haskell,$x 会突然看起来像一个拼接。
      • 你发现了all函数:filter (all conditions) [1..10]
      • 还有 any 函数,具体取决于您要如何组合谓词:any p = 或 .地图 p;所有 p = 和 .地图 p;
      • @Peter:所有类型都有 (a -> Bool) -> [a] -> Bool,而不是 [(a -> Bool)] -> a -> Bool。
      • 仍然可以输入:all ($ x) conditions,而不是 and $ map ($ x) conditions
      【解决方案4】:

      如果你有一个a -&gt; Bool 类型的过滤函数列表,并且想将它们组合成一个相同类型的简洁过滤函数,我们可以编写函数来做。您使用以下两个函数中的哪一个将取决于您需要的过滤器行为。

      anyfilt :: [(a -> Bool)] -> (a -> Bool)
      anyfilt fns = \el -> any (\fn -> fn el) fns
      
      allfilt :: [(a -> Bool)] -> (a -> Bool)
      allfilt fns = \el -> all (\fn -> fn el) fns
      

      anyfilt 将在任何过滤器函数返回 true 时返回 true,如果所有过滤器函数返回 false,则返回 false。 allfilt 将在所有过滤器函数都返回 true 时返回 true,而 如果任何过滤器函数返回 false 则返回 false。 请注意,您不能对任一函数进行 η-reduce,因为 RHS 上对 fns 的引用位于匿名函数中。

      像这样使用它:

      filterLines :: [String] -> [String]
      filterLines = let
        isComment = isPrefixOf "# "
        isBlank = (==) ""
        badLine = anyfilt([isComment, isBlank])
        in filter (not . badLine)
      
      main = mapM_ putStrLn $ filterLines ["# comment", "", "true line"]
      --> "true line"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-02
        • 2018-10-04
        • 2020-06-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-09
        相关资源
        最近更新 更多