在 Paul Graham 的 ANSI Common Lisp 中编写示例
谁能解释一下 Paul Graham 的 ANSI Common Lisp 第 110 页中的示例吗?
该示例尝试解释使用 &rest 和 lambda 来创建函数式编程工具。其中之一是组成函数参数的函数。我找不到任何解释它是如何工作的。代码如下:
(defun compose (&rest fns)
(destructuring-bind (fn1 . rest) (reverse fns)
#'(lambda (&rest args)
(reduce #'(lambda (v f) (funcall f v))
rest
:initial-value (apply fn1 args)))))
用法是:
(mapcar (compose #'list #'round #'sqrt)
'(4 9 16 25))
输出是:
((2) (3) (4) (5))
第 2 行和第 6 行对我来说看起来特别神奇。
Can anybody explain an example in Paul Graham's ANSI Common Lisp page 110?
The example try to explain the use &rest and lambda to create functional programming facilities. One of them is a function to compose functional arguments. I cannot find anything explaining how it worked. The code is as follows:
(defun compose (&rest fns)
(destructuring-bind (fn1 . rest) (reverse fns)
#'(lambda (&rest args)
(reduce #'(lambda (v f) (funcall f v))
rest
:initial-value (apply fn1 args)))))
The usage is:
(mapcar (compose #'list #'round #'sqrt)
'(4 9 16 25))
The output is:
((2) (3) (4) (5))
Line 2 and 6 look especially like magic to me.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
compose
函数返回一个 闭包,调用每个从最后到第一个函数,将每个函数调用的结果传递给下一个函数。调用
(compose #'list #'round #'sqrt)
产生的闭包首先计算其参数的平方根,将结果四舍五入到最接近的整数,然后创建结果列表。以 3 作为参数调用闭包相当于计算(list (round (sqrt 3)))
。destructuring-bind 评估
(reverse fns)
表达式以相反的顺序获取compose
的参数,并将结果列表的第一项绑定到 fn1局部变量和结果列表的其余部分到 rest 局部变量。因此,fn1 保存 fns 的最后一项,#'sqrt
。reduce 使用累积结果调用每个
fns
函数。:initial-value (apply fn1 args)
为reduce
函数提供初始值,并支持使用多个参数调用闭包。不需要多个参数,compose
可以简化为:The
compose
function returns a closure that calls each of the functions from last to first, passing on the result of each function call to the next.The closure resulting from calling
(compose #'list #'round #'sqrt)
first calculates the square root of its argument, rounds the result to the nearest integer, then creates a list of the result. Calling the closure with say 3 as argument is equivalent to evaluating(list (round (sqrt 3)))
.The destructuring-bind evaluates the
(reverse fns)
expression to get the arguments ofcompose
in reverse order, and binds its first item of the resulting list to the fn1 local variable and the rest of the resulting list to the rest local variable. Hence fn1 holds the last item of fns,#'sqrt
.The reduce calls each the
fns
functions with the accumulated result. The:initial-value (apply fn1 args)
provides the initial value to thereduce
function and supports calling the closure with multiple arguments. Without the requirement of multiple arguments,compose
can be simplified to:destructuring-bind
结合具有绑定的析构函数。析构函数是一个允许您访问数据结构的一部分的函数。car
和cdr
是简单的析构函数,用于提取列表的头部和尾部。getf
是一个通用的析构函数框架。绑定最常由let
执行。在此示例中,fns
为(#'list #'round #'sqrt)
(compose
的参数),因此(反向 fns)
是(#'sqrt #'round #'list)
。 当然, Then相当于
除了它不绑定
tmp
之外。 destructuring-bind 的思想是它是一个模式匹配构造:它的第一个参数是数据必须匹配的模式,并且模式中的符号绑定到相应的数据片段。所以现在
fn1
是#'sqrt
,rest
是(#'round #'list)
。compose
函数返回一个函数:(lambda (&rest args) ...)
。现在考虑一下当您将该函数应用于某些参数(例如4
)时会发生什么。可以应用 lambda,产生apply
< /a> 函数将fn1
应用于参数;由于此参数不是列表,因此它只是(#'sqrt 4)
,即2
。换句话说,我们现在有了
reduce
< /a> 函数完成其工作,即依次将#'(lambda (vf) (funcall fv))
应用于#'round
和#'list
,从2
开始。这相当于destructuring-bind
combines destructors with binding. A destructor is a function that lets you access a part of a data structure.car
andcdr
are simple destructors to extract the head and tail of a list.getf
is a general destructor framework. Binding is most commonly performed bylet
. In this example,fns
is(#'list #'round #'sqrt)
(the arguments tocompose
), so(reverse fns)
is(#'sqrt #'round #'list)
. Thenis equivalent to
except that it doesn't bind
tmp
, of course. The idea ofdestructuring-bind
is that it's a pattern matching construct: its first argument is a pattern that the data must match, and symbols in the pattern are bound to the corresponding pieces of the data.So now
fn1
is#'sqrt
andrest
is(#'round #'list)
. Thecompose
function returns a function:(lambda (&rest args) ...)
. Now consider what happens when you apply that function to some argument such as4
. The lambda can be applied, yieldingThe
apply
function appliesfn1
to the argument; since this argument is not a list, this is just(#'sqrt 4)
which is2
. In other words, we haveNow the
reduce
function does its job, which is to apply#'(lambda (v f) (funcall f v))
successively to the#'round
and to#'list
, starting with2
. This is equivalent to好的,这里是:
(#'sqrt #'round #'list)
),然后将第一项粘贴到fn1 中
,其余的进入rest
。我们有:fn1
=#'sqrt
和rest
=(#'round #'list)
。(apply sqrt args)
(其中args
是赋予结果 lambda 的值)作为初始值,并且每次迭代都会获取下一个函数从rest
调用。(round (apply sqrt args))
,第二次迭代最终得到(list (round (apply sqrt args)))< /代码>。
Okay, here goes:
(#'sqrt #'round #'list)
), then sticks the first item intofn1
, and the rest intorest
. We have:fn1
=#'sqrt
, andrest
=(#'round #'list)
.(apply sqrt args)
(whereargs
are the values given to the resulting lambda) as the initial value, and with each iteration grabbing the next function fromrest
to call.(round (apply sqrt args))
, and the second iteration you end up with(list (round (apply sqrt args)))
.sqrt
in your case) is allowed to take multiple arguments. The rest of the functions are called with single arguments only, even if any particular function in the chain does a multiple-value return.这个例子难倒了我一天。我终于可以通过重命名一些参数并在每一行有意义之前对其进行注释来理解它。以下是帮助我向自己解释的内容。
在书中使用调用的示例中:
参数functions变为(#'LIST #'ROUND #'SQRT)
This example stumped me for a day. I could finally understand it by renaming some of the arguments and commenting each line before it made sense. Below is what helped me explain it to myself.
In the book example using the call:
The parameter functions becomes (#'LIST #'ROUND #'SQRT)