lomeo: (лямбда)
Dmitry Antonyuk ([personal profile] lomeo) wrote2007-05-21 03:58 pm

Задачка со свёрткой

Читал статью Bernie Pope "Getting a Fix from the Right Fold" из The Monad. Reader Issue 6. В ней решается задачка о написании dropWhile через foldr. В частности, эта задачка решалась в Graham Hutton "A Tutorial on the Universality and Expressiveness of Fold" через таплы.

У меня удивительно быстро получилось решить эту задачку. Решение было эквивалентным решению 2 в статье.


Смысл в том, чтобы породить функцию с помощью foldr, которую мы уже затем применим к списку. Функция или вернёт тот же список или обрежет у него хвост, т.е. id (tail (tail (tail (tail (tail xs))))).

myDropWhile p xs = foldr f id xs xs
    where
        f x g xs | p x       = g (tail xs)
                 | otherwise = xs


К сожалению, это решение неоптимально, т.к. функция, порожденная foldr (та самая к которой мы второй раз применяем список), зависит от количества сброшенных элементов. Pope предлагает решение, которое вместо порождения цепочки функций, попеременно применяет функцию к списку. Для этого достаточно, чтобы tail был вызван раньше g, который является рекурсивным вызовом foldr. С помощью паттерн-матчинга (который как известно неленив) мы получим:

myDropWhile p xs = foldr f id xs xs
    where
        f x g xs@(_:ys) | p x       = g ys
                        | otherwise = xs


Я бы не догадался. Мне пришлось бы расписывать пример, как проходит вычисление, чтобы до этого дойти. Как он это сообразил, не пойму... Он то, конечно, говорит, мол, Эврика! и всё такое...

[identity profile] thesz.livejournal.com 2007-05-21 12:49 pm (UTC)(link)
А что означает xs(_@ys)?

[identity profile] lomeo.livejournal.com 2007-05-21 01:20 pm (UTC)(link)
Бляха-муха, быстро писал.
Исправил, спасибо.

[identity profile] thesz.livejournal.com 2007-05-21 01:23 pm (UTC)(link)
А он маньяк, судя по всему. ;)

Форсирование вычислений паттернами я тоже делал, но тут как-то... жестковато, что-ли. ;)

[identity profile] lomeo.livejournal.com 2007-05-21 01:26 pm (UTC)(link)
Ах, и ты тоже! ;-)

[identity profile] thesz.livejournal.com 2007-05-21 01:30 pm (UTC)(link)
"Кто, кроме меня?
Если не я, то кто же?" (C) чьи-то стихи.

;)

[identity profile] lomeo.livejournal.com 2007-05-21 01:37 pm (UTC)(link)
А ты тоже в таких случаях говоришь "Эврика!"?

[identity profile] thesz.livejournal.com 2007-05-21 01:41 pm (UTC)(link)
Ну, у меня случаи попроще.

Я просто тупо форсирую вычисления. ;)

[identity profile] deni-ok.livejournal.com 2007-05-21 01:36 pm (UTC)(link)
Я думаю, имелось ввиду xs@(_:ys)

[identity profile] deni-ok.livejournal.com 2007-05-21 01:37 pm (UTC)(link)
О! Уже исправили :)

[identity profile] lomeo.livejournal.com 2007-05-21 01:38 pm (UTC)(link)
Да.. быстро писал, а в ЖЖ ещё компилер хаскелевый не встроили, подлецы.

[identity profile] deni-ok.livejournal.com 2007-05-21 01:50 pm (UTC)(link)
От них даже интерпретатора фиг дождёшься :)

Кстати (точнее совсем не в тему), нарыл сегодня, ещё не разбирался толком:

Automatic generation of free theorems: http://haskell.as9x.info/index.html

Какой-то немец и документация подробная по-немецки.

[identity profile] lomeo.livejournal.com 2007-05-21 02:07 pm (UTC)(link)
Ага, знаком - я с онлайновой версией баловался.
Там всё из "теоремы нахаляву" - две модели - обычная и с fixpoint, точно по Вадлеру.

Хорошая вещь, в закладках.

[identity profile] deni-ok.livejournal.com 2007-05-21 02:10 pm (UTC)(link)
Угу, и у меня там же :)

[identity profile] rvp74.livejournal.com 2007-05-21 02:20 pm (UTC)(link)
Мое решение, которое пришло мне сходу в первую минуту:

myDropWhile p xs = res 
 where
  (_,res) = foldr f ([],[]) xs
  f x (acc,res) | p x       = (x:acc,res)
                | otherwise = (x:acc,x:acc)

[identity profile] lomeo.livejournal.com 2007-05-21 02:24 pm (UTC)(link)
Я его просто уже знал - оно из Graham Hutton "A Tutorial on the Universality and Expressiveness of Fold".
Поэтому мне было интересно сделать по другому :)

[identity profile] deni-ok.livejournal.com 2007-05-21 04:30 pm (UTC)(link)
> Как он это сообразил, не пойму... Он то, конечно, говорит, мол, Эврика! и всё такое...

Он там дальше раскрывает тайну:

the functional programming coprocessor in my head

:-)

[identity profile] lomeo.livejournal.com 2007-05-22 04:26 am (UTC)(link)
Где такие ставят? :)

[identity profile] deni-ok.livejournal.com 2007-05-22 08:39 am (UTC)(link)
Я думаю где-то в Англии. Или в Шотландии. :)

[identity profile] eraplee.livejournal.com 2007-05-21 06:33 pm (UTC)(link)
или я чето не понял, или решение просто :)

mdropWhile p l = foldr (\x (y:ys) -> if p y then ys else (y:ys)) l [0..length l - 1]

Там ещё было дополнительное требование

[identity profile] deni-ok.livejournal.com 2007-05-21 07:49 pm (UTC)(link)
It is also highly desirable that the non-strictness of dropWhile is preserved. For instance, it should still be able to return results on infinite lists:

Hugs> take 3 (dropWhile (<5) [1..])
[5,6,7]

Вызов length портит non-strictness.

[identity profile] deni-ok.livejournal.com 2007-05-21 07:53 pm (UTC)(link)
А вообще да, неожиданное решение!

[identity profile] lomeo.livejournal.com 2007-05-22 04:30 am (UTC)(link)
О, интересно.

[identity profile] migmit.livejournal.com 2007-05-22 07:46 am (UTC)(link)
Забавно... Я, по чисто эстетическим причинам, почти всегда использую второй вариант.

[identity profile] lomeo.livejournal.com 2007-05-22 07:52 am (UTC)(link)
Угу, может быть и я тоже его использовал бы, но только разницы не заметил :(
Вообще, когда начинаются разговоры о том, что вот здесь лениво, а тут strict, поэтому семантика нарушается, это ещё достаточно легко поглядеть глазом, но когда от этого зависит производительность, приходится брать в руку ручку. Я про себя, разумеется.