2016-03-14 4 views
1

Скажите, что я хотел, чтобы удалить все нули в конце списка:Как шаблон сопоставить конец списка?

removeEndingZeros :: (Num a, Eq a) => [a] -> [a] 
removeEndingZeros (xs ++ [0]) = removeEndingZeros xs 
removeEndingZeros xs   = xs 

Это не работает из-за (++) оператора в аргументе. Как определить конец списка с помощью сопоставления с образцом?

+2

Возможный дубликат [Можете ли вы использовать сопоставление образцов для привязки последнего элемента списка?] (Http://stackoverflow.com/questions/7576843/can-you-use-pattern-matching-to-bind-the -last-element-of-a-list) –

+0

Как насчет того, чтобы обратить вспять проблему, вы можете удалить ведущие нули? И если вы не ограничены использованием списков, то может использоваться Data.Sequence. – epsilonhalbe

ответ

7

Eсти функция в Data.List, чтобы сделать это:

dropWhileEnd :: (a -> Bool) -> [a] -> [a] 
dropWhileEnd p = foldr (\x xs -> if p x && null xs then [] else x : xs) [] 

Таким образом, вы можете отбросить конечные нули с

dropWhileEnd (== 0) 

Другой, очень похоже, функция может быть реализована следующим образом:

dropWhileEnd2 :: (a -> Bool) -> [a] -> [a] 
dropWhileEnd2 p = foldr (\x xs -> if null xs && p x then [] else x : xs) [] 

dropWhileEnd2 p имеет точно такую ​​же семантику, как reverse . dropWhile p . reverse, но может быть разумно ожидать, чтобы быть быстрее постоянным множителем. dropWhileEnd имеет разные, несопоставимые свойства строгости, чем другие (в некоторых случаях он более строгий и менее строгий).

Можете ли вы выяснить обстоятельства, при которых можно ожидать, что каждый будет быстрее?

6

В стандартном Haskell вы не можете сопоставить образ в конце списка. Из-за того, как определяются списки, доступ к концу списка занимает время, линейное по длине списка. Так как вы не можете оставить завершающие нули без двух прохождений в худшем случае, очевидное решение хорошо - падение ведущих нулей с обратной стороны списка, а затем обратным снова, чтобы восстановить первоначальный порядок:

removeEndingZeros = reverse . dropWhile (== 0) . reverse 
+0

Я предполагаю, что это следствие того, как определяются списки. Спасибо за окончательный ответ. –

+1

@MarkAnastos На самом деле вы можете сделать это за один проход с некоторой уловкой. См. Ответ dfeuer. – Carl

+0

Carl - Как 'dropWhileEnd' не требует двух проходов? Похоже, что 'foldr' проходит список до конца, после чего список реконструируется. Если я чего-то не упускаю, он сохраняет только один обход. В любом случае, это, безусловно, лучшее решение, чем у меня :) –

Смежные вопросы