Using tree shorthand in Lisp

To collapse a flat list in Lisp, you use reduce:

* (reduce #'+ '(1 2 3 4 5))
15

But what if I have an arbitrarily complex tree and I want to apply a function on each element? So, to collapse on '(1 (2) (3 (4) 5)), will it still be 15? I tried to use it reduce, but I had to provide a custom function that seemed to hit the target:

(defun tree+ (a b)
  (cond ((null b) 0)
        ((atom b) (+ a b))
        (t (+ (tree+ a (car b))
              (tree+ 0 (cdr b))))))

(reduce #'tree+ '(1 (2) (3 (4) 5)) :initial-value 0) ; returns 15

Of course, I could smooth the list first, but it reduceis a common function, sometimes you have to keep the structure and order of the original sequence. For example, mapand filtercan be implemented using reduce. What if I wanted to write an implementation my-mapbased on reduceso that:

(my-map '1+ '(1 (2 (3) 4) 5)) ; outputs '(2 (3 (4) 5) 6)

reduce ? ?

+1
3

treeduce , , . Wikipedia, Fold ( ), :

fold - -, , , , - , , . , , node , , , . , .

:

List ::= Cons(Object, List)
       | Nil

, Cons Nil .

Cons(x,Cons(y,Cons(z,Nil)))

Fn(x,Fn(y,Fn(z,init)))

Nil init ,

Fn(x,Fn(y,Fn(z,init())))

, . :

Tree ::= Node(Tree,Tree)
       | Leaf(Object)

, : Node Leaf. :

TreeReduce(nodeFn,leafFn,tree) =
  case tree of 
    Node(left,right) => nodeFn(TreeReduce(nodeFn,leafFn,left),TreeReduce(nodeFn,leafFn,right)
    Leaf(object) => leafFn(object)

Common Lisp :

(defun tree-reduce (node-fn leaf-fn tree)
  (if (consp tree)
      (funcall node-fn 
               (tree-reduce node-fn leaf-fn (car tree))
               (tree-reduce node-fn leaf-fn (cdr tree)))
      (funcall leaf-fn 
               tree)))
 
(tree-reduce 'cons
             (lambda (x) 
               (if (numberp x) (1+ x) x))
             '(1 (2 3) (4 5 6)))
;=> (2 (3 4) (5 6 7))

tree-reduce, , :

(tree-reduce '+
             (lambda (x)
               (if (null x) 0 x))
             '(1 (2) (3 (4) 5)))
;=> 15

, null, - , , cons, , nil , (1 (2. 3) 4. 5), (1 (2 3) 4 (5)) ( (1 (2 3, nil) 4 (5 nil). nil), ).

+4

Lisp map reduce. , , , tree-equal subst.

, - :

(defun reduce-tree (function tree &key (key #'identity))
  (if (atom tree)
      (funcall key tree)
      (funcall function
               (reduce-tree function (car tree) :key key)
               (reduce-tree function (cdr tree) :key key))))

:

> (reduce-tree #'+ '(1 . ((2 . 3) . ((4 . 5) . 6))))
==> 21
> (reduce-tree #'+ '(1 (2) (3 (4) 5)) :key (lambda (x) (or x 0)))
==> 15
+2

tree-reduce , .

, :

(defun tree+ (a b)
  (cond ((null b) 0)
        ((atom b) (+ a b))
        (t (+ (tree+ a (car b))
              (tree+ 0 (cdr b))))))

(reduce #'tree+ '(1 (2) (3 (4) 5)) :initial-value 0)

, #'tree+ reduce, tree+ . , + .

, tree+ , .

lambda, Y-combinator , , labels :

(defun tree-reducer (binary-func &optional initial-val)
  (labels ((tr-red (a b)
             (cond ((null b) initial-val) 
                   ((atom b) (funcall binary-func a b))
                   (t (+ (tr-red a (car b))
                         (tr-red initial-val (cdr b)))))))
     #'tr-red))

:

(reduce (tree-reducer #'+ 0) '(1 (2) (3 (4) 5)) :initial-value 0)  -> 15

, , , , tree-reducer, ! , :

(reduce (tree-reducer #'+ 0) '((1 (2) (3 (4) 5))) :initial-value 0) -> 15

15? reduce! , , ((1 (2) ...))) 0, .

, tree-reducer , (, ).

(reduce (tree-reducer #'+ 0) '(1 (2) (3 (4) 5)) :initial-value 1) -> 16  ;; OK

(reduce (tree-reducer #'+ 1) '(1 (2) (3 (4) 5)) :initial-value 0) -> 20  ;; Whoa!
0

All Articles