Обходное дерево с объективом и молниями

Я изучаю пакет Lens. Я должен сказать, что это довольно сложная задача.

Может кто-нибудь показать мне, как пересечь дерево с молнией из объектива? В частности, как я могу написать функцию, которая принимает список корней и позволяет мне получить доступ к ветвям поддерева?

Предположим, что у меня есть это дерево. Если мой ввод [1, 3], функция должна позволить мне получить доступ к node 10 и 11.

import Control.Lens
import Data.Tree
import Data.Tree.Lens

testTree = Node 1 [ Node 2 [ Node 4 [ Node 6 [], Node 8 [] ],
                             Node 5 [ Node 7 [], Node 9 [] ] ],
                    Node 3 [ Node 10 [], 
                             Node 11 [] ] 
                  ]

zipperTree = zipper testTree

Кроме того, как именно я использую saveTape и restoreTape для сохранения пути обхода (к StateT или IORef)?

Ответы

Ответ 1

edit: Я обычно экспериментирую в ghci, чтобы понять новый код, поэтому для таких, как я, я создал School of Haskell post/page, который приходит с приведенными ниже примерами, но они доступны для редактирования и запуска.


Подумайте, что эти примеры ответят на ваши вопросы, но для целесообразности я собираюсь изменить другой node. Мои знания о функциях застежки-молнии в lens довольно мелкий. Это займет немного больше времени, чтобы читать и привыкать к типам в lens по сравнению со многими другими пакетами, но потом это неплохо. Я не использовал модуль молнии или модуль дерева в пакете объектива перед этим сообщением.

Деревья не очень хорошо сочетаются с show, поэтому, если у меня будет время, я вернусь и добавлю довольно красивую распечатку, иначе это, вероятно, ключ к работе в repl с этими примерами, чтобы увидеть, что происходит.

Просмотр

Если я хочу просмотреть значение первого node, в соответствии с пакетом дерева для дерева, это называется корнем, то вы можете:

zipperTree & downward root & view focus

Изменение

Чтобы изменить это значение и воссоздать дерево (rezip дерево):

zipperTree & downward root & focus .~ 10 & rezip

Если вы хотите переместиться вниз по веткам, вам нужно использовать downward branches. Вот пример, который изменяет корень первой ветки и rezipx дерево:

zipperTree & downward branches 
           & fromWithin traverse 
           & downward root 
           & focus .~ 5 
           & rezip

Здесь я перехожу вниз в список ветвей. Затем я использую fromWithin use use traverse для перемещения по списку, если это был кортеж, я мог бы использовать both вместо этого.

Сохранение и воспроизведение дорожек обхода

saveTape и restoreTape позволяют сохранить позицию на молнии, чтобы ее можно было восстановить последним.

Сохранить позицию:

tape = zipperTree & downward branches 
                  & fromWithin traverse 
                  & downward root 
                  & saveTape

Затем, чтобы воссоздать обход через дерево, я могу:

t <- (restoreTape tape testTree)

Затем вы можете использовать t в качестве новой молнии и изменить ее как обычно:

t & focus .~ 15 & rezip

Лента повторяет шаги, которые вы сделали, поэтому может работать на других деревьях, поэтому последующие действия будут работать с лентой, как определено выше:

testTree2 = Node 1 [ Node 2 [] ]
t2 <- (restoreTape tape testTree2)
t2 & focus .~ 25 & rezip

Изменение нескольких местоположений

Если вы хотите изменить несколько корней, просто удерживайте их при изменении размера молнии. Ниже перечислены два корня testTree2:

zipper testTree2 & downward root 
                 & focus .~ 11 
                 & upward 
                 & downward branches 
                 & fromWithin traverse 
                 & downward root 
                 & focus .~ 111 
                 & rezip

Ответ 2

По моему опыту, вы обычно не хотите Zipper. В этом случае вы можете определить Traversal, который позволит вам получить доступ к подпоследовательности с учетом пути корневых узлов.

-- Make things a little easier to read
leaf :: a -> Tree a
leaf = return

testForest :: Forest Int
testForest = [ Node 1 [ Node 2 [ Node 4 [ leaf 6, leaf 8]
                               , Node 5 [ leaf 7, leaf 9]]
                      , Node 3 [ leaf 10, leaf 11]]]

-- | Handy version of foldr with arguments flipped
foldEndo :: (a -> b -> b) -> [a] -> b -> b
foldEndo f xs z = foldr f z xs

-- | Traverse the subforests under a given path specified by the values of
-- the parent nodes.
path :: Eq a => [a] -> Traversal' (Forest a) (Forest a)
path = foldEndo $ \k ->     -- for each key in the list
       traverse               -- traverse each tree in the forest
     . filtered (hasRoot k)   -- traverse a tree when the root matches
     . branches               -- traverse the subforest of the tree

  where
  hasRoot :: Eq a => a -> Tree a -> Bool
  hasRoot k t = k == view root t

-- | Helper function for looking at 'Forest's.
look :: Show a => Forest a -> IO ()
look = putStr . drawForest . (fmap . fmap) show

-- Just show 'path' in action

demo1 = look $ testForest & path [1,3] .~ []
demo2 = look $ testForest & path [1,3] . traverse . root *~ 2
demo3 = look $ testForest ^. path [1,3]
demo4 = [] == testForest ^. path [9,3]
demo5 = traverseOf_ (path [1,3]) print testForest