mapcar的lambda中的Lisp操作顺序

mapcar的lambda中的Lisp操作顺序,lisp,common-lisp,Lisp,Common Lisp,我正在Linux中运行clisp,并在书中练习ANSI Common Lisp。其中一种方法是使用mapcar创建一个函数,该函数获取一个整数列表,并返回一个列表,其中每个元素都是原始元素及其在列表中的索引。因此,如果我执行(foo'(0 0)),它将生成(0 1 2),等等 我尝试的是: (defun foo (lst) (let ((c 0)) (mapcar #'(lambda (x) (+ x c) (incf c)) lst))) 当我运行(foo'(0 0))时得到的是

我正在Linux中运行
clisp
,并在书中练习ANSI Common Lisp。其中一种方法是使用
mapcar
创建一个函数,该函数获取一个整数列表,并返回一个列表,其中每个元素都是原始元素及其在列表中的索引。因此,如果我执行
(foo'(0 0))
,它将生成
(0 1 2)
,等等

我尝试的是:

(defun foo (lst)
  (let ((c 0))
    (mapcar #'(lambda (x) (+ x c) (incf c)) lst)))
当我运行
(foo'(0 0))
时得到的是
(1 2 3)
。作为一个实验,我交换了增量的顺序,并这样定义它:

(defun foo (lst)
  (let ((c 0))
    (mapcar #'(lambda (x) (incf c) (+ x c)) lst)))
我得到了完全相同的结果(在本例中,我希望
(13)
,但在前一种情况中不是这样)。我还尝试过像这样包装序列
(progn(+xc)(incf c))
,得到了相同的结果。要使函数工作,我需要执行
(let((c-1))
。为什么在这种情况下,
(incf c)
(+x c)
的顺序似乎并不重要?看来不管怎样,
(incf c)
总是先完成,但我不知道为什么。我知道我在这里遗漏了一些基本的东西,所以这应该是LISP pro的一个简单快捷的答案,向我解释为什么会这样做。:)


谢谢。

在第一个示例中,lambda函数的结果是最后一种形式的结果,
(incf c)
<代码>(+xC)被忽略

在第二个示例中,第一个增量是c,因此第一个数字加1,第二个数字加2,以此类推

对于第一个示例,还可以使用
prog1
返回第一个表单的值,而不是最后一个表单的值:

(defun foo (lst)
  (let ((c 0))
    (mapcar
     (lambda (e) (prog1 (+ e c) (incf c)))
     lst)))

在第一个示例中,lambda函数的结果是最后一种形式的结果,
(incf c)
<代码>(+xC)被忽略

在第二个示例中,第一个增量是c,因此第一个数字加1,第二个数字加2,以此类推

对于第一个示例,还可以使用
prog1
返回第一个表单的值,而不是最后一个表单的值:

(defun foo (lst)
  (let ((c 0))
    (mapcar
     (lambda (e) (prog1 (+ e c) (incf c)))
     lst)))

在您的第一个lambda中,
(lambda(x)(+xc)(incf c))
,您正在执行无副作用的加法并丢弃其值。然后递增
c
,并将其新值作为lambda中的值返回


尝试调用
(foo'(10 10 10))
并查看返回值是什么,这两个版本将返回
(1 2 3)
(11 12 13)
在您的第一个lambda中,
(lambda(x)(+x c)(incf c))
,您正在执行无副作用的加法并丢弃其值。然后递增
c
,并将其新值作为lambda中的值返回

尝试调用
(foo'(10 10 10))
并查看返回值,这两个版本将返回
(1 2 3)
(11 12 13)

注意:这旨在探索实现这一点的一些方法。您在问题中提到了其中一些解决方案(例如,从-1开始),我的意思不是将它们包括在这里,以表明您没有考虑它们。这个答案更多的是关于在面向值的语言中考虑增量前后操作符的作用

请注意,
(incf c)
不仅执行副作用(顺序很重要),而且还返回
c
的新值。也就是说,
incf
是一个预增量运算符。这意味着编写
(+x(incf c))
是完全可以接受的,如:

(defun foo (lst)
  (let ((c 0))
    (mapcar #'(lambda (x)
                (+ x (incf c)))
            lst)))

(foo '(0 0 0))
;=> (1 2 3)
现在,这些结果并不可怕,但它们不会将元素的索引添加到元素中,因为索引从零开始,而不是从一开始。您可以做一个简单的更改并添加减法:

(defun foo (lst)
  (let ((c 0))
    (mapcar #'(lambda (x)
                (+ x (1- (incf c)))) ; or (+ x -1 (incf c)), etc.
            lst)))

(foo '(0 0 0))
;=> (0 1 2)
在我看来,甚至比这更好的是使用第一种形式,只需在
-1
处开始
c
。不管怎么说,我可能会这么做,你在问题中提到这是一个选择:

(defun foo (lst)
  (let ((c -1))
    (mapcar #'(lambda (x)
                (+ x (incf c)))
            lst)))

(foo '(0 0 0))
;=> (0 1 2)
不过,我们可能会问,是否有一种方法可以近似增量后的运算符,这样我们就不必(在代码中)看到减法了?也就是说,我们可以将上述内容视为某些语言中的内容:

result.add( x + ++c )
问我们为什么不能这么做

result.add( x + c++ ) 
相反?这很简单:

(defmacro post-incf (place)
  `(1- (incf ,place)))

(defun foo (lst)
  (let ((c 0))
    (mapcar #'(lambda (x)
                (+ x (post-incf c)))
            lst)))

(foo '(0 0 0))
;=> (0 1 2)
实际上,类C语言的前置和后置增量运算符没有灵活性,但在常见的Lisp中,通过使用可选参数,可以提供多一点灵活性,其中第二个可选参数可用于指定值的更改量。调整
post incf
来处理这样的第二个参数并不难。第一次尝试可能是:

(defmacro post-incf (place &optional (delta-form 1))
  `(- (incf ,place ,delta-form) ,delta-form))
这对于常量值(如
1
)来说效果很好,但它会多次计算
增量形式,我们不希望这样(因为它可能有副作用,或者计算成本很高)。我们只需要更仔细地评估一次,因此:

(defmacro post-incf (place &optional (delta-form 1))
  (let ((delta (gensym (symbol-name '#:delta-))))
    `(let ((,delta ,delta-form))
       (- (incf ,place ,delta) ,delta))))

(let ((a 0))
  (list a                ; => 0
        (incf a 3)       ; => 3
        (post-incf a 2)  ; => 3 (but x is now 5)
        a))              ; => 5
;=> (0 3 3 5)
注意:这旨在探索实现这一目标的一些方法。您在问题中提到了其中一些解决方案(例如,从-1开始),我的意思不是将它们包括在这里,以表明您没有考虑它们。这个答案更多的是关于在面向值的语言中考虑增量前后操作符的作用

请注意,
(incf c)
不仅执行副作用(顺序很重要),而且还返回
c
的新值。也就是说,
incf
是一个预增量运算符。这意味着编写
(+x(incf c))
是完全可以接受的,如:

(defun foo (lst)
  (let ((c 0))
    (mapcar #'(lambda (x)
                (+ x (incf c)))
            lst)))

(foo '(0 0 0))
;=> (1 2 3)
现在,这些结果并不可怕,但它们不会将元素的索引添加到元素中,因为索引从零开始,而不是从一开始。您可以做一个简单的更改并添加减法:

(defun foo (lst)
  (let ((c 0))
    (mapcar #'(lambda (x)
                (+ x (1- (incf c)))) ; or (+ x -1 (incf c)), etc.
            lst)))

(foo '(0 0 0))
;=> (0 1 2)
在我看来,甚至比这更好的是使用第一种形式,只需在
-1
处开始
c
。不管怎样,这是我可能会做的,你在你的问题中注意到这是