![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Я это как-то писал, но напишу ещё раз, бо тема поднялась.
Недостатки явной рекурсии по сравнению с комбинаторами (ага, zip3):
Юный хаскеллист, избегай явной рекурсии!
Ну и ссылочка, куда без неё!
Недостатки явной рекурсии по сравнению с комбинаторами (ага, zip3):
- Рекурсия непонятна (согласен, это субъективное).
- Обычно используется с декомпозицией, нарушая инкапусляцию.
- Всегда используется для работы с элементами, вместо работы с коллекцией (wholemeal programming).
- Цепочка вызовов проще для оптимизации.
- Сложнее нежно мною любимый equational reasoning.
Юный хаскеллист, избегай явной рекурсии!
Ну и ссылочка, куда без неё!
no subject
Date: 2012-11-30 09:53 pm (UTC)Где, грубо говоря, file -- это исходник, а module -- директория. Нужно написать функцию, которая применит функцию компиляции к каждому исходному файлу, причём в правильном порядке, с учётом что от чего зависит.
Для данного примера порядок может быть таким: f1, f2, f4, f5, f3, f6, f0, f7.
В идеале ещё бы обнаружить циклические зависимости.
Как это сделать двойной рекурсией -- очевидно, а вот комбинаторного решения я придумать не могу.
no subject
Date: 2012-11-30 10:09 pm (UTC)http://www.haskell.org/haskellwiki/Scrap_your_boilerplate
(есть ещё более навороченные и/или удобные варианты, более поздние)
Например, одной строкой можно получить все файлы. Другой - все зависимости. Отображение (map compile) на файлы даст компиляцию и тп.
Наверное, это ближе всего к нужному тебе.
no subject
Date: 2012-11-30 10:27 pm (UTC)no subject
Date: 2012-11-30 10:28 pm (UTC)no subject
Date: 2012-11-30 10:42 pm (UTC)Сортируем пары вида (файл, зависимость) вот таким сравнением:
compareTopo (fa,depsa) (fb,depsb)
| fa `elem` depsb && fb `elem` depsa = error "cycle!"
| fa `elem` depsb = LT
| otherwise = EQ
sortDeps = sortBy compareTopo filesDeps
Первыми должны оказаться файлы без зависимостей.
Не однострочник, но близко.
no subject
Date: 2012-11-30 10:59 pm (UTC)Единственное, не могу сообразить, как scrap your boilerplate позволит мне одной строкой собрать из дерева все зависимости всех файлов, но верю на слово, что такое возможно.
no subject
Date: 2012-11-30 11:33 pm (UTC)Выхлоп: Похоже на правду.
no subject
Date: 2012-12-01 02:39 am (UTC)А на циклы проверяли? Что-то меня смущает проверка на циклы. По идее зависимости должны раскрываться, но они тут раскрываться будут бесконечно в случае цикла. Банально f0 = File "f0" [f0].
no subject
Date: 2012-12-01 10:54 am (UTC)(no subject)
From:no subject
Date: 2012-12-01 11:06 am (UTC)В принципе, всё очевидно: двойная рекурсия спрятана в двух вложенных через map listify, которые, в свою очередь (если я ничего не путаю), просто выполняют flatten дерева.
Тогда, если можно, встречный теоретический вопрос: а как правильно в хаскеле оценить производительность алгоритма, учитывая ленивость языка и автоматическую мемоизацию? Например, в императивном языке в таком решении достаточно паскудная сложность, и двойная рекурсия будет несравнимо оптимальней.
no subject
Date: 2012-12-01 12:03 pm (UTC)Я, на самом деле, не большой спец по SYB. Я пользуюсь им довольно редко, только когда уж совсем нудно становится. Кстати, есть более быстрая и удобная версия SYB, называется uniplate: http://community.haskell.org/~ndm/uniplate/
Мой пример слишком простой - берём всё файлы из структуры и просеиваем через nub, убирая повторы. То есть, мы можем напороться на циклы, это раз, и мы сделаем слишком много действий, это два.
Сам listify сделан через обобщённую свёртку - gfold. В этой свёртке накапливается список - все подэлементы некоторого значения тестируются на принадлежность к File, добавляются в список и по ним делается listify, результат которого снова добавляется к результату. Чуть изменив listify, передавая ему на вход уже обнаруженные элементы, мы можем избавиться от циклов и не делать лишней работы по повторному проходу уже известных элементов.
no subject
Date: 2012-12-03 04:10 pm (UTC)Реквестирую решение на лиспе, т.к. haskell пока не понимаю
no subject
Date: 2012-12-01 04:05 am (UTC)no subject
Date: 2012-12-01 10:53 am (UTC)У вас появляется шанс поправить и понять, что не так. ;)
Поправите?
no subject
Date: 2012-12-01 10:47 pm (UTC)(no subject)
From:(no subject)
From:no subject
Date: 2012-11-30 11:51 pm (UTC)Он на ней деньги собирался зарабатывать. ;)
no subject
Date: 2012-12-01 12:17 am (UTC)Идея такая:
1. Строим список пар (имя объекта, непосредственные зависимости). Для файла это зависимости, для модуля - имена компонентов.
2. Строим дерево зависимостей, т.е. разворачиваем непосредственные зависимости.
3. Сортируем очевидным образом (зависящий идёт позже, если независимы - по имени)
4. Вуаля
Код без учёта циклических зависимостей: http://hpaste.org/78574
Для учёта идея проста, мы храним "родителей" и смотрим, чтобы текущий от них не зависел: http://hpaste.org/78575
Update
Я был не прав в пункте 3, сортировать не нужно. Вот конечный результат: http://hpaste.org/78585
no subject
Date: 2012-12-01 09:50 am (UTC)Индуктивные типы — это деревья. Графы делают из них и напрямую, без соответствующего вычислятельного контекста, индуктивными типами они не являются.
Тем не менее, для графов можно сделать соответствующие комбинаторы.
Первыми ну просто напрашиваются всякие преобразования в процессе обхода графа в ширину-глубину.
Ещё строят построение (ленивого) дерева обхода и что-то-там с ним дальше делают.
no subject
Date: 2012-12-01 11:13 am (UTC)Последнее решение я, правда, уже не особо понимаю, но это чисто из-за слабого знания содержимого стандартной библиотеки хаскеля.
no subject
Date: 2012-12-01 10:42 pm (UTC)no subject
Date: 2012-12-01 11:11 am (UTC)http://hpaste.org/78589 (http://hpaste.org/78589)
no subject
Date: 2012-12-01 08:38 pm (UTC)no subject
Date: 2012-12-01 10:57 pm (UTC)http://hpaste.org/78604
Data.Functor.Fixedpoint находится в unification-fd, импорт можно заменить на ровно две строчки:
newtype Fix f = Fix { unFix :: f (Fix f) }
cata phi = phi . fmap self . unFix
no subject
Date: 2012-12-02 04:03 am (UTC)no subject
Date: 2012-12-01 10:25 pm (UTC)но для того, чтобы избежать явной рекурсии это не обязательно. например, в данном случае берем рекурсивный алгоритм из википедии (http://en.wikipedia.org/wiki/Topological_sort, второй) и пишем его в лоб: http://hpaste.org/78601
основная функция получилась так:
Код сурово императивный, что видно по foldr, но хотя бы нет явной структурной рекурсии.
Как известно, вместо рекурсии можно использовать цикл с очередью (http://hpaste.org/78602):
Код по-прежнему императивный, но явной рекурсии больше нет. Она спрятана в until и foldr.
Если все немного обобщить (разрешить зависимости и на уровне модулей, чтобы структура была более симметричная) и причесать, то можно избавиться от рекурсии и во flatten, перенеся ее в общий комбинатор foldTree: http://hpaste.org/78603