免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 9029 | 回复: 16
打印 上一主题 下一主题

能不能讲一下closure,正在看practical common lisp,就是理解不了这个概念 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-03-08 23:39 |只看该作者 |倒序浏览
书上的例子是
(let ((count 0)) #'(lambda () (setf count (1+ count))))

the reference to count inside the LAMBDA form should be legal according to the rules of lexical scoping. Yet the anonymous function containing the reference will be returned as the value of the LET form and can be invoked, via FUNCALL, by code that’s not in the scope of the LET. So what happens? As it turns out, when count is a lexical variable, it just works. The binding of count created when the flow of control entered the LET form will stick around for as long as needed, in this case for as long as someone holds onto a reference to the function object returned by the LET form. The anonymous function is called a closure because it “closes over” the binding created by the LET.
The key thing to understand about closures is that it’s the binding, not the value of the variable, that’s captured. Thus, a closure can not only access the value of the variables it closes over but can also assign new values that will persist between calls to the closure. For instance, you can capture the closure created by the previous expression in a global variable like this:

(defparameter *fn* (let ((count 0)) #'(lambda () (setf count (1+ count)))))

Then each time you invoke it, the value of count will increase by one.

CL-USER> (funcall *fn*)
1
CL-USER> (funcall *fn*)
2
CL-USER> (funcall *fn*)
3
基本上没看明白,而且红色的地方也有点奇怪

[ 本帖最后由 xdshting 于 2009-3-8 23:44 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2009-03-09 00:04 |只看该作者
1. let 为 count 绑定了一个值 0,返回的匿名函数可以引用,修改它。这称为 static scope (lexical),类似于 c 语言中的静态变量。粗略地讲,闭包包住了一个私有环境。

2. 返回的匿名函数若与变量 *fn* 绑定,则可以通过 *fn* 来调用,访问并修改匿名函数的“私有环境”。

3. lisp 支持自动垃圾回收,当匿名函数不再被使用时,资源会被回收。(... will stick around for as long as needed ...)

4. 1+ 是个函数,对参数执行加 1 操作.

论坛徽章:
0
3 [报告]
发表于 2009-03-09 00:18 |只看该作者
scheme 是第一个引入静态作用域的 lisp,后来 Guy Steel 跑去开发 common lisp,把这个也加进去了。

有一些 lisp 是动态作用域的,比如 elisp. 如果在 emacs 中执行


  1. (setq *fn* (let ((count 0)) #'(lambda () (setq count (1+ count)))))


  2. (funcall *fn*)
复制代码


就会出错,因为: Debugger entered--Lisp error: (void-variable count)

显然 *fn* 找不到 count 了。如果在函数外面定义一个 count,值为 100


  1. (setq *fn* (let ((count 0)) #'(lambda () (setq count (1+ count)))))

  2. (setq count 100)

  3. (funcall *fn*)
复制代码


则 funcall 的结果为 101,显然跟 let 里头的 count 是无关的。

[ 本帖最后由 win_hate 于 2009-3-9 00:19 编辑 ]

论坛徽章:
0
4 [报告]
发表于 2009-03-09 00:22 |只看该作者
每次运行(funcall (let ((count 0)) #'(lambda () (setf count (1+ count))))) 的值都是1

(defparameter *fn* (let ((count 0)) #'(lambda () (setf count (1+ count)))))

Then each time you invoke it, the value of count will increase by one.

CL-USER> (funcall *fn*)
1
CL-USER> (funcall *fn*)
2
CL-USER> (funcall *fn*)
3
这怎么解释,红色部分不就是说用defparameter代替后面的(let...)吗?
另外
(1+ count)与(+ 1 count)有区别吗?

谢谢

[ 本帖最后由 xdshting 于 2009-3-9 00:23 编辑 ]

论坛徽章:
0
5 [报告]
发表于 2009-03-09 00:34 |只看该作者
1、

  1. 每次运行(funcall (let ((count 0)) #'(lambda () (setf count (1+ count))))) 的值都是1
复制代码

因为每次运行都得到一个新的匿名函数,它也包有一个新的环境,在该环境中,count 值为 0。

2、

  1. (defparameter *fn* (let ((count 0)) #'(lambda () (setf count (1+ count)))))
复制代码

以后通过 *fn* 去调用,let 只执行了一次,只有一个与 *fn* 关联的匿名函数(相应的私有空间)。

看看下面的演示


  1. [1]> (defparameter f (let ((count 0)) #'(lambda () (setf count (1+ count)))))F
  2. [2]> (defparameter g (let ((count 0)) #'(lambda () (setf count (1+ count)))))
  3. G
  4. [3]> (funcall f)
  5. 1
  6. [4]> (funcall f)
  7. 2
  8. [5]> (funcall f)
  9. 3
  10. [6]> (funcall g)
  11. 1
  12. [7]>
复制代码


f, g 的私有空间是不同的。直接 funcall let 的结果相当于每次都生成一个新函数,所以值也都是 1 了。

3、1+ 是个函数名字,这个函数接受一个参数;+ 是另外一个函数,接受 0 个或多个参数.


  1. > (funcall '1+ 2)
  2. 3
  3. > (funcall '+ 1 2)
  4. 3
  5. > (funcall '+ 2 1)
  6. 3
复制代码


(1+ count)与(+ 1 count)的结果是一样的,但调用的是不同的函数。

论坛徽章:
0
6 [报告]
发表于 2009-03-09 00:50 |只看该作者
谢谢,现在明白一些了

看到您用
(funcall '+ 3 4)
我用
(funcall #'+ 3 4)

可以得到相同的结果,我觉得你用的形式不对阿,为什么能执行?

论坛徽章:
0
7 [报告]
发表于 2009-03-09 01:05 |只看该作者
原帖由 xdshting 于 2009-3-9 00:50 发表
谢谢,现在明白一些了

看到您用
(funcall '+ 3 4)
我用
(funcall #'+ 3 4)

可以得到相同的结果,我觉得你用的形式不对阿,为什么能执行?


参考这个连接,http://www.lispworks.com/documen ... c/Body/f_funcal.htm

Syntax:

funcall function &rest args => result*

......

funcall applies function to args. If function is a symbol, it is coerced to a function as if by finding its functional value in the global environment.


所以两种形式都是可以的。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
8 [报告]
发表于 2009-03-09 09:06 |只看该作者
1+ 是个函数名称呢?
还是仅仅只是 + 这个函数 curry 化的结果?

论坛徽章:
0
9 [报告]
发表于 2009-03-09 09:22 |只看该作者
谢谢了
再看ansi common lisp关于closure的介绍,又有一点不明白的地方
书上的例子
( let ((counter 0))
    (defun reset ()
       (setf counter 0))
    (defun stamp ()
       (setf counter (+ counter 1))))

CL-USER> ( l i s t (stamp) (stamp) ( reset ) (stamp))
          ( 1 2 0 1)

list是怎么看到stamp和reset函数的?这两个函数在let内部定义的阿

[ 本帖最后由 xdshting 于 2009-3-9 09:38 编辑 ]

论坛徽章:
0
10 [报告]
发表于 2009-03-09 10:46 |只看该作者
原帖由 flw 于 2009-3-9 09:06 发表
1+ 是个函数名称呢?
还是仅仅只是 + 这个函数 curry 化的结果?


lisp 不支持自动 curry 化,1+ 是专门另做的一个函数。当然本质上可以看成 1+x 的 curry

在 emacs 中,对 1+ 的描述为:

1+ is a built-in function in `C source code'.

(1+ NUMBER)

Return NUMBER plus one.  NUMBER may be a number or a marker.
Markers are converted to integers.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP