Заимствование в значительной степени от myself: проблема с таким вопросом заключается в том, что вы не знаете, какой элемент удалить, пока не дойдете до конца списка. Как только мы это наблюдаем, самое прямолинейное дело - переместить список в один конец, затем обратно, используя foldr
(второй обход происходит из-за того, что foldr
не является хвостовым рекурсивным).
Чистое решение, о котором я могу думать, состоит в том, чтобы перестроить список на пути назад, отбросив первый элемент.
cutLast :: Eq a => (a -> Bool) -> [a] -> Either [a] [a]
cutLast f = foldr go (Left [])
where
go x (Right xs) = Right (x:xs)
go x (Left xs) | f x = Right xs
| otherwise = Left (x:xs)
Возвращаемый тип Either
различать что-либо отказаться от списка (Left
) не найдено, и встретив и бросил последний удовлетворительный элемент из списка (Right
). Конечно, если вы не заботитесь о упали ли вы или не уронить элемент, вы можете оставить эту информацию:
cutLast' f = either id id . cutLast f
После обсуждения скорости в комментариях, я попытался выгрузив Either [a] [a]
для (Bool,[a])
. Без какой-либо дополнительной настройки это (как прогнозировалось @dfeuer) последовательно немного медленнее (порядка 10%).
Используя неопровержимые шаблоны на кортеже, мы действительно можем избежать форсирования всего вывода (согласно предложению @ chi), что делает это намного быстрее для ленивого запроса вывода. Это код, который:
cutLast' :: Eq a => (a -> Bool) -> [a] -> (Bool,[a])
cutLast' f = foldr go (False,[])
where
go x ~(b,xs) | not (f x) = (b,x:xs)
| not b = (False,x:xs)
| otherwise = (True,xs)
Однако это от 2 до 3 раз медленнее, чем любой из двух других версий (которые не используют Неопровержимые) при вынужденных к нормальной форме.
Какое поведение вы ожидаете, если нет элементов, удовлетворяющих условию? Вернуть список как есть? – Alec
@Alec: Правильно. – Erakura
Возможный дубликат [Haskell - Последний элемент фильтра] (http://stackoverflow.com/questions/31864951/haskell-filter-last-element) – amalloy