Reputation: 4188
I got a tree:
(A . ((C . ((D . nil)(E . nil)))
(B . ((F . nil)(G . nil)))))
I want to transform this tree into:
((A C D) (A C E) (A B F) (A B G))
I already implemented this function for doing so:
(defun tree->paths (tree &optional buff)
(labels ((recurse (follow-ups extended-list)
(if follow-ups
(append (list (tree->paths (car follow-ups) extended-list))
(recurse (cdr follow-ups) extended-list))
nil)))
(rstyu:aif (cdr tree)
(recurse it (append buff (list (car tree))))
(append buff (list (car tree))))))
But applying it results in:
(tree->paths '(A . ((C . ((D . nil) (E . nil)))
(B . ((F . nil) (G . nil))))))
=>
(((A C D) (A C E)) ((A B F) (A B G)))
I must be missing some kind of append/merge within the recursion but I am not seeing it.
Upvotes: 1
Views: 121
Reputation:
Here, I've tried to rewrite it so that it would work linearly (because your original function would exhaust stack space). However, while doing so, I've discovered something, which you might consider in general re' your original idea:
(defun tree-to-paths (tree)
(loop with node = tree
with trackback = nil
with result = nil
with head = nil
with head-trackback = nil
while (or node trackback) do
(cond
((null node)
(setf node (car trackback)
trackback (cdr trackback)
result (cons head result)
head (car head-trackback)
head-trackback (cdr head-trackback)))
((consp (car node))
(setf trackback (cons (cdr node) trackback)
head-trackback (cons head head-trackback)
head (copy-list head)
node (car node)))
(t (setf head (cons (car node) head)
node (cdr node))))
finally (return (nreverse (mapcar #'nreverse result)))))
In your example data the result you want to receive seems intuitively correct, but you can think of it also as if there were more paths, such as for example:
A -> C -> NIL
- From looking at your data, this result seems redundant, but in general, you may want to have these results too / it would be hard to filter them all out in general.
Upvotes: 0
Reputation: 41170
You must remove the list
in (append (list (tree->paths
The tree->paths
returns a list of paths; so does recurse
. So, they may be appended without wrapping in a list
call.
Upvotes: 1
Reputation: 4188
I started over and chose the reverse approach by going leaf to root instead of root to leaf as I tried in the question:
(defun tree->paths2 (tree)
(labels ((recurse (follow-ups)
(if follow-ups
(append (tree->paths2 (car follow-ups))
(recurse (cdr follow-ups)))
nil)))
(rstyu:aif (cdr tree)
(mapcar #'(lambda(arg)
(cons (car tree) arg))
(recurse it))
(list tree))))
(tree->paths2 '(A . ((C . ((D . nil) (E . nil)))
(B . ((F . nil) (G . nil))))))
=>
((A C D) (A C E) (A B F) (A B G))
But if there is a way to fix my first approach I'd prefer to accept such fix as an answer.
Upvotes: 0