Yatima2975, кажется, охватил ваши первые два вопроса, я постараюсь ответить на третий. Для этого я рассмотрю нереально простой случай, но я уверен, что вы сможете представить что-то более реалистичное.
Представьте, что вы хотите вычислить глубину полного двоичного дерева порядка . Тип (немаркированных) двоичных деревьев (в синтаксисе Haskell):N
type Tree = Leaf | Node Tree Tree
Теперь полное дерево порядка :N
full : Int -> Tree
full n | n == 0 = Leaf
full n = Node (full (n-1)) (full (n-1))
И глубина дерева вычисляется
depth : Tree -> Int
depth Leaf = 0
depth (Node t1 t2) = 1 + max (depth t1) (depth t2)
Теперь вы можете видеть, что любое вычисление сначала построит полное дерево порядка используя а затем разложит это дерево, используя . Вырубка лесов основывается на наблюдении, что такой шаблон (конструкция, сопровождаемая деконструкцией) часто может быть закорочена : мы можем заменить любые вызовы одним вызовом :н е у л л д е р т ч д е р т ч ( е ¯u л л н ) е ц л л _ д е р т чд е п т ч ( ф у л л н) Nеу л лд е р т чд е п т ч ( ф у л л н) еu l l _ d e p t h
full_depth : Int -> Int
full_depth n | n == 0 = 0
full_depth n = 1 + max (full_depth (n-1)) (full_depth (n-1))
Это позволяет избежать выделения памяти для полного дерева и необходимости выполнять сопоставление с образцом, что значительно повышает производительность. Кроме того, если вы добавите оптимизацию
max t t --> t
Затем вы превратили процедуру экспоненциального времени в линейную процедуру времени ... Было бы здорово, если бы была дополнительная оптимизация, которая признала бы, что был тождеством на целых числах, но я не уверен, что какой-либо такой Оптимизация используется на практике.еu l l _ d e p t h
Единственный основной компилятор, который выполняет автоматическую вырубку лесов, - это GHC, и, если я правильно помню, это выполняется только при составлении встроенных функций (по техническим причинам).
Во-первых, списки - это своего рода деревья. Если мы представляем список в виде связанного списка , это просто дерево , каждый узел которого имеет 1 или 0 потомков.
Деревья разбора - это просто использование деревьев в качестве структуры данных. Деревья имеют множество приложений в информатике, включая сортировку, реализацию карт, ассоциативные массивы и т. Д.
В общем, список, деревья и т. Д. Являются рекурсивными структурами данных: каждый узел содержит некоторую информацию и другой экземпляр той же структуры данных. Складывание - это операция над всеми такими структурами, которая рекурсивно преобразует узлы в значения «снизу вверх». Развертывание - это обратный процесс, он преобразует значения в узлы «сверху вниз».
Для заданной структуры данных мы можем механически построить их функции свертывания и разворачивания.
В качестве примера возьмем списки. (Я буду использовать Haskell для примеров, так как он напечатан, и его синтаксис очень чистый.) Список - это либо конец, либо значение, либо «хвост».
Теперь давайте представим, что мы свернули список. На каждом шаге у нас есть текущий узел, который нужно свернуть, и мы уже свернули его рекурсивные подузлы. Мы можем представить это состояние как
где
r
промежуточное значение, построенное путем складывания подсписка. Это позволяет нам выразить функцию сворачивания над списками:Мы конвертируем
List
вListF
, рекурсивно сворачивая его подсписок, а затем используем функцию, определенную вListF
. Если вы думаете об этом, это просто еще одно представление стандартаfoldr
:Мы можем построить
unfoldList
таким же образом:Опять же, это просто еще одно представление
unfoldr
:(Обратите внимание, что
Maybe (a, r)
это изоморфноListF a r
.)И мы можем построить функцию обезлесения тоже:
Он просто исключает промежуточное звено
List
и объединяет функции складывания и раскладывания.Та же процедура может быть применена к любой рекурсивной структуре данных. Например, дерево, узлы которого могут иметь 0, 1, 2 или потомков со значениями на 1- или 0-ветвящихся узлах:
Конечно, мы можем создавать
deforestTree
так же механически, как и раньше.(Обычно мы выражаемся
treeFold
более удобно как:)
Я опущу детали, я надеюсь, что картина очевидна.
Смотрите также:
источник
Это немного сбивает с толку, но вырубка лесов применяется (во время компиляции) для устранения промежуточных деревьев, которые будут созданы (во время выполнения). Вырубка лесов не предполагает взлома частей абстрактного синтаксического дерева (это устранение мертвых веток :-)
Еще одна вещь, которая может вас отбросить, это то, что списки - это деревья, просто очень несбалансированные!
источник