17

レンズパッケージを学んでいます。私はそれがかなり挑戦的な仕事だと言わなければなりません。

誰かがレンズからジッパーで木を横断する方法を教えてもらえますか?特に、ルートのリストを取得してサブツリーのブランチにアクセスできるようにする関数を作成するにはどうすればよいですか?

私がこの木を持っているとしましょう。入力が[1, 3]、の場合、関数はノード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

さらに、トラバーサルパスを(StateTまたはIORefに)どの程度正確に使用saveTapeして保存しますか?restoreTape

4

2 に答える 2

17

編集:私は通常、新しいコードを理解するためにghciで実験しているので、私のような人のために、以下の例が付属するSchool of Haskellの投稿/ページを作成しましたが、編集可能で実行可能です。


この例で質問に答えられると思いますが、便宜上、別のノードを変更します。レンズのジッパー機能についての私の知識 はかなり浅いです。他の多くのパッケージと比較して、レンズパッケージのタイプを読んで慣れるのに少し時間がかかり ますが、その後は悪くありません。この投稿の前は、レンズパッケージのジッパーモジュールやツリーモジュールを使用していませんでした。

木はかなりうまくいっていないshowので、時間があれば戻ってきて、かなり印刷されたものを追加します。そうでない場合は、これらの例を使用して、何が起こっているかを確認することが重要です。

表示

最初のノードの値を表示したい場合、ツリーレンズパッケージによると、これはルートと呼ばれ、次のことができます。

zipperTree & downward root & view focus

変更

その値を変更してツリーを再作成するには(ツリーを再圧縮します):

zipperTree & downward root & focus .~ 10 & rezip

ブランチを下に移動したい場合は、を使用する必要がありますdownward branches。最初のブランチのルートを変更し、ツリーを再圧縮する例を次に示します。

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

ここで、ブランチのリストに移動します。次に、use fromWithinuseを使用traverseしてリストをトラバースします。これがタプルの場合は、both代わりに使用できます。

トラバーサルパスの保存と再生

saveTaperestoreTape後で復元できるように、ジッパー内の位置を保存できるようにします。

位置を保存します:

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の2つのルートを変更します。

zipper testTree2 & downward root 
                 & focus .~ 11 
                 & upward 
                 & downward branches 
                 & fromWithin traverse 
                 & downward root 
                 & focus .~ 111 
                 & rezip
于 2013-03-19T01:01:08.790 に答える
4

私の経験では、通常、ジッパーは必要ありません。この場合、ルートノードのパスを指定してサブフォレストにアクセスできるようにするトラバーサルを定義できます。

-- 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
于 2013-05-09T22:58:41.963 に答える