【问题标题】:How function snd in Haskell works in filterHaskell中的函数snd如何在过滤器中工作
【发布时间】:2019-11-05 09:24:28
【问题描述】:

我将此代码输入到 ghci

Prelude> filter snd [('a',True),('b',True),('c',False),('d',True)]

为什么会返回

[('a',True),('b',True),('d',True)]

而不是

[('a',True),('c',False),('d',True)]

snd函数返回第二项,那为什么不filter snd过滤第二项呢?

【问题讨论】:

  • 你能解释一下你为什么期望这个结果吗?我无法理解您期望 filter snd 使用的逻辑。
  • @RobinZigmond:我认为 Jesse 认为这意味着“过滤掉每一秒的元素”,如一个保留、一个移除、一个保留……
  • @WillemVanOnsem 不,只是列表中的第二个。他们添加了一个示例。
  • 这里在 this 用例中,snd(\ x -> snd x == True) 相同,与 (\ (_,b) -> b == True) 相同,这对您来说可能更清楚。但当然只有snd(与(\ (_,b) -> b) 相同)是首选拼写。隐含的(== True)filter 语义的一部分,即filter 的工作原理。

标签: list haskell filter filtering predicate


【解决方案1】:

您希望filter 从该表达式的colloquial sense 的列表中“过滤掉”元素,因此filter snd 会删除第二项。

这不是它的工作原理

如果你是对的,filter snd [1,2,3] 将评估为[1,3]。相反,它不进行类型检查,因为 snd 适用于元组,而不是数字。

那么,它是如何工作的?

filter f [item1, item2, ...] 返回所有items 的列表,其中f itemTrue

例如,filter even [1,2,3,4] 返回[2,4]

由于snd ('b', True) 的计算结果为True,因此在您的示例中filter 将在结果中包含(b, True)。同理,(c, False) 将被省略

【讨论】:

    【解决方案2】:

    简而言之filter snd 保留 2 元组,其中元组的第二项是 True

    filter :: (a -> Bool) -> [a] -> [a] 将一个函数作为参数,该函数将a 类型的元素映射到Bool。如果BoolTrue,它将在结果中保留原始列表的元素,否则该元素将不会成为结果的一部分。

    filter 因此过滤elementwise:它确实考虑列表中的下一个或前一个元素。它只是检查元素上的谓词是否满足。

    由于您在这里有一个 2 元组列表,其中每个元组的第二项是 Bool,因此 snd :: (a, b) -> b 将因此将每个元素映射到第二个元素,从而保留第二个元素的 2 元组2 元组中的 True。因此,filter snd 的最通用类型是 filter snd :: [(a, Bool)] -> [(a, Bool)],因为 2 元组的第二项应该是 Bool

    因此,这意味着filter snd 确实会像这样过滤:

    Prelude> filter snd [('a',True),('b',True),('c',False),('d',True)]
    [('a',True),('b',True),('d',True)]
    

    我们可以通过显式递归过滤掉每隔一个元素,例如:

    filterAtEven :: [a] -> [a]
    filterAtEven [] = []
    filterAtEven (x:xs) = x : filterAtOdd xs
    
    filterAtOdd :: [a] -> [a]
    filterAtOdd [] = []
    filterAtOdd (_:xs) = filterAtEven xs
    

    例如:

    Prelude> filterAtEven [('a',True),('b',True),('c',False),('d',True)]
    [('a',True),('c',False)]
    Prelude> filterAtOdd [('a',True),('b',True),('c',False),('d',True)]
    [('b',True),('d',True)]
    

    或者如果你想删除特定的索引,我们可以使用ilist包中的deleteAt :: Int -> [a] -> [a]

    Prelude> import Data.List.Index
    Prelude Data.List.Index> deleteAt 2 [('a',True),('b',True),('c',False),('d',True)]
    [('a',True),('b',True),('d',True)]
    

    或者我们可以自己实现:

    deleteAt :: Int -> [a] -> [a]
    deleteAt i | i < 0 = id
               | otherwise = go i
      where go _ [] = []
            go 0 (_:xs) = xs
            go n (x:xs) = x : go (n-1) xs
    

    【讨论】:

      【解决方案3】:

      您的filter 函数根据每对的第二个值过滤您的列表。这就是('c',False) 被过滤掉的原因...

      【讨论】:

      • 所以它过滤第二个值,为什么返回['a','b','c','d']?我的意思是,它过滤每一对的所有秒数。
      • @Jesse: no filter snd 在不是 2 元组的元素列表上,或者没有第二项是 Bool 的 2 元组是没有意义的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-08
      • 1970-01-01
      • 2012-10-19
      • 2015-12-22
      • 1970-01-01
      • 2017-11-27
      • 2015-04-02
      相关资源
      最近更新 更多