Как функция snd в Haskell работает в фильтре - PullRequest
3 голосов
/ 23 июня 2019

Я набираю этот код в 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 отфильтровать второй элемент?

Ответы [ 3 ]

8 голосов
/ 23 июня 2019

Вы ожидаете, что filter "отфильтрует" элементы из списка в разговорном смысле этого выражения и, таким образом, filter snd удалит второй элемент.

То естьне как это работает

Если бы вы были правы, filter snd [1,2,3] оценил бы в [1,3].Вместо этого он не выполняет проверку типов, поскольку snd работает с кортежами, а не с числами.

Итак, как это работает?

filter f [item1, item2, ...] возвращает список всех items, для которых f item равно True

Например, filter even [1,2,3,4] возвращает [2,4]

Поскольку snd ('b', True) оценивается как True, в вашем примере filter будет включать(b, True) в результате.Точно так же, (c, False) будет опущено

7 голосов
/ 23 июня 2019

Короче говоря : filter snd сохраняет 2 кортежа, где второй элемент кортежа равен True.

filter :: (a -> Bool) -> [a] -> [a] принимает в качестве параметра функцию, которая отображает элементы типа a в Bool.В случае, если Bool равно True, в результате будет сохранен элемент исходного списка, в противном случае этот элемент не будет частью результата.

filter, таким образом, фильтрует поэлементно : не учитывает следующий или предыдущий элемент (ы) в списке.Он просто проверяет, удовлетворен ли предикат для элемента.

Поскольку у вас здесь есть список из двух кортежей, где вторым элементом каждого кортежа является Bool, то snd :: (a, b) -> b, таким образом, отобразит каждый элемент на втором элементе и, таким образом, сохранит 2 кортежа, где второй элемент 2 кортежа равен True.Таким образом, самый общий тип filter snd - это filter snd :: [(a, Bool)] -> [(a, Bool)], поскольку второй элемент из двух кортежей должен быть 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)]

Или, если вы хотите удалить определенный индекс, мы можем использовать deleteAt :: Int -> [a] -> [a] пакета ilist:

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
4 голосов
/ 23 июня 2019

Ваша функция filter фильтрует ваш список по второму значению каждой пары.Вот почему ('c',False) отфильтровывается ...

...