首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >关于Lisp中的引文

关于Lisp中的引文
EN

Stack Overflow用户
提问于 2013-11-25 14:40:55
回答 2查看 118关注 0票数 0

我是李斯特的新手,业余时间学习很慢.几个月前,来自Lisp的错误报告使我感到困惑,因为以下表达式不起作用:

((if (> 2 1) + -) 1 2)

环顾四周,我知道Lisp不是Scheme...in Lisp,我需要这样做:

(funcall (if (> 2 1) '+ '-) 2 1),或

(funcall (if (> 2 1) #'+ #'-) 2 1)

我还浏览了关于lisp-1和lisp-2的介绍性材料,虽然我无法吸收there...in的全部内容,但我知道quote会阻止评估,作为评估规则的例外。

最近,我正在读一些关于reduce...and的文章,然后作为一个练习,我想写我自己版本的reduce。虽然我设法让它正常工作(至少它看起来很有效),但我意识到,在defun的主体中,我仍然无法确切地解释为什么某些地方需要funcall,而在某些地方则不需要。

以下是elisp中的myreduce

代码语言:javascript
复制
    (defun myreduce (fn v lst)
    (cond ((null lst) v)
          ((atom lst) (funcall fn v lst))
          (t (funcall fn (car lst) (myreduce fn v (cdr lst))))))

(myreduce '+ 0 '(1 2 3 4))

我的问题是关于第三行和第四行:

  1. 第三行:为什么我需要funcall?为什么不只是(fn v lst)?我的“论点”是,在(fn v lst)中,fn是列表中的第一个元素,因此lisp可能能够使用这个位置信息将其作为一个function...but来处理,而不是。所以我肯定漏掉了一些东西。
  2. myreduce递归调用的第4行:将什么类型的fn传递给对myreduce'++的递归调用或其他什么?

我想应该有一些我不知道的非常基本的东西.我想知道,当我调用myreduce时,如第6/最后一行所示,之后到底发生了什么(至少是关于'+是如何传递的),以及在任何REPL环境中有什么方法来跟踪吗?

非常感谢,

/bruin

EN

回答 2

Stack Overflow用户

发布于 2013-11-25 15:08:14

通用Lisp是一个LISP-2,有两个名称空间。一个用于函数,另一个用于变量。参数绑定在变量命名空间中,因此函数命名空间中不存在fn

代码语言:javascript
复制
(fn arg) ; call what fn is in the function namespace
(funcall fn ...) ; call a function referenced as a variable

'+是一个符号,当funcallapply看到它是一个符号而不是一个函数对象时,它将在全局函数命名空间中查找它。#'+(function +)的缩写,它从本地函数命名空间解析函数。对于大量调用,#'+'+更快,因为'+需要查找。符号和函数都可以作为fn传递给myreduce,所传递的内容与第4行中传递的内容相同。

代码语言:javascript
复制
(myreduce '+ 0 '(1 2 3 4)) ; here funcall might lookup what '+ is every time (CLISP does it while SBLC caches it)
(myreduce #'+ 0 '(1 2 3 4)); here funcall will be given a function object looked up in the first call in all consecutive calls

现在,如果您通过'+,它将被计算到+并绑定到fn。在myreduce中,我们在递归中传递fn,它也将被求值为+

对于#'+,它计算函数并绑定到fn。在myreduce中,我们在递归中传递fn,它将被计算到变量命名空间中绑定到的函数对象fn

公共Lisp需要构造来添加函数命名空间。例如:

代码语言:javascript
复制
(flet ((double (x) (+ x x))) ; make double in the function namespace
  (double 10)) ; ==> 20

但是您可以编写它并在变量命名空间中使用它:

代码语言:javascript
复制
(let ((double #'(lambda (x) (+ x x)))) ; make double in the variable namespace
  (funcall double 10))
票数 4
EN

Stack Overflow用户

发布于 2013-11-25 16:20:37

通用Lisp有两个名称空间(实际上超过两个):一个用于变量,另一个用于函数。这意味着,根据上下文的不同,一个名称可以有不同的含义:它可以是变量,也可以是函数名。

代码语言:javascript
复制
(let ((foo 42))    ; a variable FOO
  (flet ((foo (n) (+ n 107)))   ; a function FOO
    (foo foo)))    ; calling function FOO with the value of the variable FOO

定义变量的一些示例:

代码语言:javascript
复制
(defun foo (n) ...)   ; n is a variable
(let ((n 3)) ...)     ; n is a variable
(defparameter *n* 41) ; *n* is a variable

因此,每当定义和使用变量时,名称都位于变量命名空间中。

界定了以下职能:

代码语言:javascript
复制
 (defun foo (n) ...)         ; FOO is a function
 (flet ((foo (n) ...)) ...)  ; FOO is a function

因此,每当定义和使用函数时,名称都位于函数命名空间中。

因为函数本身是一个对象,所以函数可以是一个变量值。如果要调用这样的值,则需要使用FUNCALL或APPLY。

代码语言:javascript
复制
  (let ((plus (function plus)))
    (funcall plus 10 11)) 

为什么事情是这样的?-)

  • 两个名称空间允许我们使用名称作为变量,这些变量已经是函数。

例:在Lisp-1中,我不能写:

代码语言:javascript
复制
(defun list-me (list) (list list))

在通用Lisp中,上述代码没有冲突。

  • 单独的函数命名空间使编译后的代码更加简单:

在调用(foo 42)中,名称FOO只能是未定义的,或者是函数。不存在另一种选择。因此,在运行时,我们不必检查FOO的函数值,因为它实际上是一个函数对象。如果FOO有一个函数值,那么它必须是一个函数对象。原因是:在公共Lisp中,不可能用函数以外的东西来定义函数。

在“计划”中,您可以写:

代码语言:javascript
复制
(let ((list 42))
  (list 1 2 3 list))

由于LIST是42,所以需要在某个点进行检查并导致错误,这不是一个函数。

在Common中,上面的代码只定义了一个变量列表,但是函数列表仍然可用。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20195831

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档