Tree 在Lisp中使用reduce覆盖树

Tree 在Lisp中使用reduce覆盖树,tree,lisp,common-lisp,fold,Tree,Lisp,Common Lisp,Fold,要在Lisp中折叠平面列表,请使用reduce: * (reduce #'+ '(1 2 3 4 5)) 15 但是,如果我有一个任意复杂的树,我想在每个元素上应用一个函数,会怎么样?这样折叠起来的”(1(2)(3(4)5))仍然会给出15?我试图使用reduce,但我必须提供一个自定义函数,这有点违背了目的: (defun tree+ (a b) (cond ((null b) 0) ((atom b) (+ a b)) (t (+ (tree+ a (c

要在Lisp中折叠平面列表,请使用
reduce

* (reduce #'+ '(1 2 3 4 5))
15
但是,如果我有一个任意复杂的树,我想在每个元素上应用一个函数,会怎么样?这样折叠起来的
”(1(2)(3(4)5))
仍然会给出
15
?我试图使用
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) ; returns 15
当然我可以先将列表展平,但是
reduce
是一个通用函数,有时必须保留原始序列的结构和顺序。例如,
map
filter
可以通过
reduce
实现。如果我想基于
reduce
,编写
mymap
的实现,以便:

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

如何在树结构上使用
reduce
?在树上应用二进制函数最通用的方法是什么?

Common Lisp没有树版本的
map
或。 事实上,我能立即记住的唯一树函数是和

但是,这样做应该不会太难:

(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

Common Lisp没有
map
或的树版本。 事实上,我能立即记住的唯一树函数是和

但是,这样做应该不会太难:

(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

我在中提供了一个treeduce函数的实现,虽然它是针对Scheme的,但这里也适用相同的原则。维基百科在文章中说:

在函数式编程中,fold–也称为reduce, 累积、聚合、压缩或注入–指一系列 分析递归数据结构的高阶函数,以及 通过使用给定的组合操作重新组合 递归地处理其组成部分,建立一个返回 价值通常情况下,褶皱具有组合功能,即顶部 数据结构的节点,以及可能要使用的一些默认值 在一定条件下。然后,折叠继续组合元素 在系统中使用该函数 对

列表数据结构可以描述为代数数据类型:

List ::= Cons(Object, List)
       | Nil
当我们用一个函数调用reduce一个list时,我们本质上是将
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)
因此,要对树进行reduce,我们需要两个函数:一个替换
节点
,另一个替换
。不过,定义相当简单:

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计算您询问的总和:

(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(4 .nIL).nIL)相同)。

< P>我提供了一个<强> TeRePe><强>函数的实现,尽管它是用于方案的,但这里使用相同的原理。维基百科在文章中说:

在函数式编程中,fold–也称为reduce, 累积、聚合、压缩或注入–指一系列 分析递归数据结构的高阶函数,以及 通过使用给定的组合操作重新组合 递归地处理其组成部分,建立一个返回 价值通常情况下,褶皱具有组合功能,即顶部 数据结构的节点,以及可能要使用的一些默认值 在一定条件下。然后,折叠继续组合元素 在系统中使用该函数 对

列表数据结构可以描述为代数数据类型:

List ::= Cons(Object, List)
       | Nil
当我们用一个函数调用reduce一个list时,我们本质上是将
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)
因此,要对树进行reduce,我们需要两个函数:一个替换
节点
,另一个替换
。不过,定义相当简单:

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计算您询问的总和:

(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(4 .nIL).nIL)是相同的。当然,

< p>除了开发<代码>树缩减< /C> >之外,一个有用的练习是尝试修复现有的代码,使其更普遍适用。

也就是说,我们接受您所拥有的:

(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+
是硬编码的,用于添加。显而易见的解决方法是将
+
函数作为函数参数进行curry运算

为了实现这一点,我们可以非常简单地将您的
树+
转换成一个有趣的