Lisp-将所有元素增加一个数字

时间:2018-10-19 02:02:00

标签: lisp common-lisp

即使我增加了该功能的值,它也会在屏幕上打印1。有没有一种方法可以通过引用来调用参数,以便在函数调用后可以使用它?

    (defparameter a 1)
    (defun foo (x)
        (+ x 1))
    (foo a)
    (print a)

2 个答案:

答案 0 :(得分:6)

您的代码不包含任何可就地修改存储位置的运算符的痕迹。变量ax都不会发生突变。

函数foo返回的数字比输入数字大1

请注意,ax是不同的变量;如果我们要递增x,则对a无效。

数字在ANSI Lisp中不是可变对象;如果我们将变量x(incf x)一起递增,则会发生计算值(+ 1 x),并将此新值存储回x变量中的情况。

ANSI Lisp函数参数使用“按值传递”,就像大多数主要的Lisp方言一样。调用(foo a)时,参数表达式a减少为一个值:它产生值1。该值不再与变量关联。它被传递到函数中,没有任何来自a的记忆。在函数内部,实例化了一个在函数本地的新变量x;它收到参数值的副本:它用1初始化。因此xa无关;它刚刚收到了相同的值。

答案 1 :(得分:2)

常规解决方案-对变量的破坏性访问

我想,你想知道的实际上是这个:

(defparameter a 1)

(defmacro foo (x)
  `(setf ,x (+ ,x 1)))  ;; this macro takes the variable given for x
                        ;; accesses its memory location using `setf`
                        ;; and changes its value  to 2
                        ;; by increasing the accessed value by 1
(foo a)                 ;; a's value becomes 2 
a                       ;; 2

但是,作为常见的Lisp初学者,请小心改变周围的疯狂价值观。尽管我不想带给您Lisp强大功能的喜悦。 :)

特殊解决方案-foo = incf

但是foo(将给定值增加1) 在常见的lisp中恰好是incf,因为经常需要将此增量加1。因此,对于这种非常特殊的情况,您可以只使用incf

(defparameter a 1)
a ;; => 1

(incf a)  ;; incf mutates a to 2 - in exactly the way foo does

a ;; => 2 ;; a ist after that changed.

请注意,还有decf将变量减1-incf的相反。

全局变量的专用解决方案

但是对于全局变量,可以使用非常普通的defparameter将值重新分配给a

(defparameter a 1)

(defparameter a (foo a))  ;; reassigns new value `2` to `a`

a ;; returns 2, since mutated now

对第一个版本的评论:

(setf <var> <new-value>)就像一个指针访问变量的存储位置并向其写入为<new-value>提供的内容。 因此,在Lisp中,您必须结合使用defmacrosetf来模仿或实际上像C或Python中那样进行按引用调用。

但是您已经从Python或C中知道,按引用调用可能会带来意料之外的副作用。由于性能原因,默认情况下Python会进行按引用调用(因为Python是经过解释的,如果每次调用一个函数进行按值调用,它将变得非常慢)。但是使用Lisp,您/您至少可以在任何时候想要比解释Python更快的幅度(好吧,我正在简化-Python当然可以称呼C函数也非常快-但是,纯Python当然可以幅度)比编译的Lisp慢)。而且您还有许多其他可能性可以对同一事物进行编程。因此,请勿尝试在Lisp中模仿Python或其他程序的函数调用行为-这是错误的方法。

学习函数编程(FP)规则,该规则试图不变异任何值,而是始终按值进行调用(就像函数foo一样)。它产生2,但使a不变。推理此类程序对大脑更友好,因此可以在许多情况下避免可避免的错误。 FP当然是与Lisp打交道时应该学习的东西。 FP是学习Lisp时所学的知识之一,而我也不想错过它。

破坏性函数仅在状态确实不可避免且需要时才有意义,尤其是在性能很重要的情况下。