Suppose I have two trees:
Goal: replace one of tree A's subtrees with tree B at a specified tree A index position. The index position starts at 0 at the root node and is depth-first. In the figure for tree A above, I have labelled all the nodes with their index to show this.
For example, (replace-subtree treeA 4 treeB)
replaces the subtree at index 4 in tree A with tree B, resulting in the tree (+ (* 5 6) (- 4 2))
:
How do I implement (replace-subtree treeA index treeB)
?
This question is somewhat related to my other question: How do I get a subtree by index?. I had great difficulty in solving it, but I eventually found a workable solution for that problem by using continuation-passing style (CPS). However, this problem here appears to be far more difficult. I am completely at loss as to how I should even begin! Implementations and clues are most welcome. I'd be particularly interested in implementations that do not use call/cc
.
EDIT
I came up with a stopgap implementation while waiting for answers. It relies on set!
, which I do not prefer.
(define (replace-subtree tree index replacement)
(define counter 0)
(define replaced #f) ; Whether or not something has been replaced.
(define (out-of-bounds-error)
(error "Index out of bounds" index))
(define (traverse-tree tree)
(cond [(null? tree)
(error "Invalid tree: ()")]
[(= counter index)
(set! counter (+ counter 1))
(set! replaced #t)
replacement]
[(pair? tree)
(set! counter (+ counter 1))
(cons (car tree)
(traverse-children (cdr tree)))]
[else
;; Possible only during the initial call to traverse-tree.
;; e.g. (replace-subtree 'not-a-list 9999 '(+ 1 2)) -> error.
(out-of-bounds-error)]))
(define (traverse-children children)
(cond [(null? children) '()]
[(list? (car children))
;; `list?` instead of `pair?` to let `traverse-tree` handle
;; invalid tree ().
;; `let*` instead of `let` to guarantee traversal of
;; first child first. In Scheme, order of evaluation of
;; `let` bindings is unspecified.
(let* ((first-child (traverse-tree (car children)))
(rest-child (traverse-children (cdr children))))
(cons first-child rest-child))]
[(= counter index)
(set! counter (+ counter 1))
(set! replaced #t)
(cons replacement
(traverse-children (cdr children)))]
[else
(set! counter (+ counter 1))
(cons (car children)
(traverse-children (cdr children)))]))
(let ([result (traverse-tree tree)])
(if replaced
result
(out-of-bounds-error))))