views:

183

answers:

4

Given a tree, I want to find the paths from the root to each leaf.

So, for this tree:

    D
   /
  B
 / \ 
A   E
 \
  C-F-G

has the following paths from root (A) to leaves (D, E, G):

(A B D), (A B E), (A C F G)

If I represent the tree above as (A (B D E) (C (F G))) then the function g does the trick:

(define (paths tree)
  (cond ((empty? tree)
         '())
        ((pair? tree)
         (map (lambda (path)
                (if (pair? path)
                    (cons (car tree) path)
                    (cons (car tree) (list path))))
              (map2 paths (cdr tree))))
        (else
         (list tree))))

(define (map2 fn lst)
  (if (empty? lst)
      '()
      (append (fn (car lst))
              (map2 fn (cdr lst)))))

But this looks all wrong. I've not had to do this kind of thinking for a while, but I feel there should be a neater way of doing it. Any ideas for a better solution (in any language) would be appreciated.


EDIT - Mapping Svante's solution into Scheme gives:

(define (paths tree)
  (if (pair? tree)
      (append-map (lambda (node)
              (map (lambda (path)
                     (cons (car tree) path))
                   (paths node)))
            (cdr tree))
      (list (list tree))))

which is much neater than my original.

A: 

I think you could define the example tree as (root left right) each one a list. So your example tree would be: (D (B (A () (C () (F () G ) ) ) E) () ) and that is easier to traverse

Ismael
I think you've misread my diagram - A is the root of the tree. Also (and this probably isn't clear from the question) a node can have more than two branches, and so I don't think your approach would work.
grifaton
I just realized that I've misread your diagram. I would say that if the g function works, you should use that :-)
Ismael
A: 

You want a tree-search algorithm. Breadth-first or depth-first traversal would both work, and it makes no difference which, in this case, since you need to crawl the entire tree. Whenever you get to a leaf, just store the current path in your results.

Greg Harman
+3  A: 

I am more fluent in Common Lisp.

(defun paths (tree)
  (if (atom tree)
      (list (list tree))
      (mapcan (lambda (node)
                (mapcar (lambda (path)
                          (cons (car tree) path))
                        (paths node)))
              (cdr tree))))

CL-USER> (paths '(A (B D E) (C (F G))))
((A B D) (A B E) (A C F G))
Svante
That's quite a bit neater - and probably more correct, given the way that it deals with a tree with a single node: (paths 'a) => ((a)), while (g 'a) => (a).Is CL's mapcan more or less equivalent to my map2 above? Do you know of an inbuilt Scheme function?
grifaton
I do not know much about Scheme, but I would have guessed that something like mapcan is available. Perhaps it is seen as simple to implement, though. Your `map2` seems equivalent in principle, but it has the wrong tail call, so it will blow the stack when the list is too long. Perhaps you should use the accumulator/reverse idiom.
Svante
Scheme's equivalent to `mapcan` is `append-map` from SRFI-1.
Nathan Sanders
+2  A: 

R5RS translation of Svante's answer:

(define (accumulate op init seq)
  (define (iter ans rest)
    (if (null? rest)
        ans
        (iter (op ans (car rest))
              (cdr rest))))
  (iter init seq))

(define (flatten seq)
  (accumulate append '() seq))

(define (flatmap op seq)
  (flatten (map op seq)))

(define (atom? x)
  (not (pair? x)))

(define (paths tree)
  (if (atom? tree)
      (list (list tree))
      (flatmap (lambda (node)
                 (map (lambda (path)
                        (cons (car tree) path))
                      (paths node)))
               (cdr tree))))
Cirno de Bergerac