【问题标题】:Filter for element in a list of lists in Haskell过滤 Haskell 列表中的元素
【发布时间】:2017-05-20 22:18:39
【问题描述】:

我建立了这个结构的列表:

[(Interger, Double)]

列表是通过对整数列表和大小完全相同的双精度列表使用压缩来创建的。

现在我想过滤 25 的双打列表。我遇到的问题是我无法访问双打以在过滤器功能中使用它们。

这可能很容易,但我是这种语言的菜鸟。我在谷歌上搜索了很多并阅读了一些其他主题,但我没有找到答案。

我明白了:

filter (<18.5) listexpression

所以我正在苦苦挣扎的是那个列表表达式。如果它是单个值的列表,这很容易。我可以在压缩之前进行过滤,但是我无法再将过滤列表中的数据连接到另一个未过滤列表。

编辑:我忘了提。这是一个工作表。我们被要求自己构建过滤器和映射函数,并且不允许使用基本 Haskell 的任何附加功能。意味着不允许导入。

【问题讨论】:

    标签: list haskell filter


    【解决方案1】:

    你可以这样做:

    Prelude> filter (\p -> (snd p) < 18.5 || (snd p) > 25) [(1, 2.3), (1, 20.0)]
    [(1,2.3)]
    

    lambda function传递给filter,即

    (\p -> (snd p) < 18.5 || (snd p) > 25)
    

    表示对于每个pp 的第二个元素必须小于 18.5 或大于 25。


    也可以这样写

    Prelude> filter (\(_, f) -> f < 18.5 || f > 25) [(1, 2.3), (1, 20.0)]
    [(1,2.3)]
    

    这里的函数表示,对于第一个值无关紧要而第二个值是 f 的任何对,f 必须小于 18.5 或大于 25。

    【讨论】:

    • 谢谢!我知道这个问题对于有更多经验的人来说很容易。我确实知道匿名函数,但我不知道“snd”。我尝试使用 (!!) 的组合访问它,但这不起作用。我会尽快接受你的回答:)
    • @Shadol 你也不需要知道snd,因为模式匹配通常是更可取的。在这种情况下绝对是首选。
    • 它甚至可以与我自己创建的过滤器功能一起使用:D
    • @leftaroundabout 如果通过“模式匹配”你指的是我在这里创建的阿克曼函数:ackerF m n | m == 0 = n+1 | m &gt; 0 &amp;&amp; n == 0 = ackerF (m-1) 1 | m &gt; 0 &amp;&amp; n &gt; 0 = ackerF (m-1) (ackerF m (n-1)) 那么我仍然不知道如何使用这些知识访问我想要的元素.
    • @Shadol:我的意思是,要获得第二个元素的元组,您只需匹配模式(_,f)。就像,通过将其写为函数参数,即在本例中为\(_, f) -&gt; f &lt; 18.5 || f &gt; 25,如答案所示。
    【解决方案2】:

    很高兴看到Ami Tavory's answer 解决了您的问题。

    但在那个答案下,你评论说:

    我尝试使用 (!!) 的组合访问它,但没有成功。

    凭借助教 [:D] 的洞察力,我猜你在 Haskell 中将 listtuple 混淆了。

    zip 返回listtuple,而(!!)list 作为(第一个)参数(因此(!!1) 采用单个list 参数),所以(!!1) 可以'不适用于zip返回的list的元素,这些元素的类型为tuple

    Prelude> :t zip
    zip :: [a] -> [b] -> [(a, b)]
    Prelude> :t (!!)
    (!!) :: [a] -> Int -> a
    Prelude> :t (!!1)
    (!!1) :: [a] -> a
    

    您已经知道fstsnd 应用于tuple

    Prelude> :t fst
    fst :: (a, b) -> a
    Prelude> :t snd
    snd :: (a, b) -> b
    

    【讨论】:

      【解决方案3】:

      使用无点样式的紧凑版本是

       filter ((>18.5).snd) listexpression
      

      这使用了函数组合运算符.,其内容为:首先将snd函数应用于列表中的一个元组以提取第二个值,然后将与18.5的比较应用于该值。

      【讨论】:

        【解决方案4】:

        只是为了一些不会咬人的品种和一些额外的信息......

        在 Haskell 中 the list type is an instance of Monad class。所以像filter 这样的列表操作可以简单地通过一元绑定操作符来实现。

        *Main> [(1,2.3),(3,21.2),(5,17.1),(4,24.4)] >>= \t -> if snd t < 25 && snd t > 18.5 then [t] else []
        [(3,21.2),(4,24.4)]
        

        Monad 就是按顺序处理包含的数据。在列表单子中,包含的数据是列表本身的值。因此,绑定运算符可以非常方便地以顺序方式访问一元值(元组列表)的包含值(元组)。

        (>>=) :: Monad m => m a -> (a -> m b) -> m b
        

        monadic 绑定操作符的类型签名声明它接受一个 monad 类型值 m a 作为第一个参数(这里是元组列表)和一个函数作为第二个参数,它接受一个纯值并返回一个 monadic 值(接受一个元组并返回一个列表中的一个元组,或者在这种情况下返回一个空列表)。

        \t -> if snd t < 25 && snd t > 18.5 then [t] else []
        

        了解如何以及为什么将列表项一一应用于所提供的函数至关重要。整个列表是一个单子值,绑定运算符访问的包含的值被传递给提供的a -&gt; m b(取一个纯值并返回单子值)类型函数。因此,应用于此函数的所有列表项都成为一元值([t] 如果条件满足或[] 如果它失败),然后由绑定运算符连接形成一个单子返回值(在这种情况下是一个列表满足在 lambda 函数中实现的条件的元组)。

        这个单子操作也可以用do 表示法实现

        do
        t <- [(1,2.3),(3,21.2),(5,17.1),(4,24.4)]
        if snd t < 25 && snd t > 18.5 then return t else []
        
        [(3,21.2),(4,24.4)]
        

        当然,这非常类似于列表推导式,它实际上是一元列表操作的语法糖。因此,让我们最后一次使用列表推导来实现它。

        *Main> [t | t <- [(1,2.3),(3,21.2),(5,17.1),(4,24.4)], snd t < 25 && snd t > 18.5]
        [(3,21.2),(4,24.4)]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-29
          • 1970-01-01
          • 2014-11-27
          • 2023-03-23
          • 1970-01-01
          相关资源
          最近更新 更多