1

I have two rose trees in Haskell of m and n nodes, respectively. I want to replace the ith node of the first tree with the jth node of the second tree.

e.g.

tree 1:

           R                                                           
  _________|____________                 
  |        |           |
  A        B           C
 / \      / \         / \
D   E    F   G       H   I

tree 2:

       r
  _____|____
  |         |       
  P         Q       
 / \      / | \      
S   T    U  V  W     

then the resulting tree of tree 1 node 7 (C) replaced with tree 2 node 4 (Q) should be (assuming indexing is pre order and starting at 0)

           R                                                           
  _________|____________                 
  |        |           |
  A        B           Q
 / \      / \        / | \
D   E    F   G      U  V  W

I have tried using a zipper, but my problem is I can't workout how to get the zipper focus to the ith element,

i.e. how can i implement a function with type:

someFunc :: Tree a -> Int -> Zipper a

that take the root of the tree and traverses it (in some order) to return the zipper focussed on the ith pre order node and the context.

from the tree library I can flatten a tree to a pre order list of values using flatten, i can change this slightly to give me a list of trees e.g.

flattenToTreeList :: Tree a -> [Tree a]
flattenToTreeList t = squish t []
  where squish (Node x ts) xs = Node x ts : foldr squish xs ts

if I could do this but with a zipper then the ith element of this list would satisfy me, but I'm lost and now going round in circles.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
ayaye
  • 11
  • 1
  • You can do this pretty easily by combining the [tree-traversals](https://hackage.haskell.org/package/tree-traversals-0.1.1.0/docs/Data-Traversable-TreeLike.html#v:levelorder) and [lens](https://hackage.haskell.org/package/lens-5.2/docs/Control-Lens-Traversal.html#v:elementOf) packages. Are you interested in the details, or is that way heavier machinery than you're currently looking for? – Carl Jan 31 '23 at 21:03
  • Thanks @Carl that sounds perfect I'll look into them. I would also be interested in the details if you are willing. thanks – ayaye Jan 31 '23 at 22:31
  • Depends what zipper library you're using. One sensible type for the zipper construction function is `Tree a -> Tree (ZipperForTree a)`, i.e. where each node of the tree contains a zipper for that node. If you have one of those, your `flattenToTreeList` gets you the rest of the way there. – Daniel Wagner Jan 31 '23 at 22:32
  • @DanielWagner I had initially rolled my own zipper first and then used the rosezipper. I've tried chaning my own zipper function like you suggested to create the tree of zippers and this worked too but slightly less neat than Carl's answer. Thanks though, this is my first question and the response has been really nice. – ayaye Feb 01 '23 at 22:25

1 Answers1

3

Well, I was a bit off in my comment. I misread the requirements a bit, but in a way that actually cuts the dependencies down a bit.

-- lens
import Control.Lens ((^?), contexts, cosmos, elementOf, ix)

-- containers
import Data.Tree (Tree(..))

-- adjunctions
import Control.Comonad.Representable.Store (peeks)

tree1 :: Tree Char
tree1 = Node 'R' [ Node 'A' [Node 'D' [], Node 'E' []]
                 , Node 'B' [Node 'F' [], Node 'G' []]
                 , Node 'C' [Node 'H' [], Node 'I' []]
                 ]

tree2 :: Tree Char
tree2 = Node 'R' [ Node 'P' [Node 'S' [], Node 'T' []]
                 , Node 'Q' [Node 'U' [], Node 'V' [], Node 'W' []]
                 ]

-- Replace subtree i in t1 with subtree j in t2
-- returns Nothing if either index doesn't exist
replace :: Int -> Tree a -> Int -> Tree a -> Maybe (Tree a)
replace i t1 j t2 = update =<< replacement
  where
    update u = peeks (const u) <$> contexts t1 ^? ix i
    replacement = t2 ^? elementOf cosmos j

main :: IO ()
main = print $ replace 7 tree1 4 tree2

This adds a direct dependency on the adjunctions package, but it's already a transitive dependency via lens. So it's an extra import, but no additional required packages. In exchange, it doesn't need to use tree-traversals at all.

This is a bit unlike usual lens code, in that neither cosmos nor contexts are especially common, but they're great tools for manipulating substructures of self-similar data types. And that's a perfect description of replacing subtrees.

This uses pretty conceptually heavy tools, but I think the meaning comes across pretty well.

Carl
  • 26,500
  • 4
  • 65
  • 86
  • 1
    Has worked beautifully for what I need. I'll need to investigate the `lens` code a little further than I have already as I've not come across `cosmos` or `contexts` before but that is not problem at all. Thanks for all the help, great result for my first stack overflow question :) – ayaye Feb 01 '23 at 22:21