【问题标题】:Lisp - Increasing all elements by a numberLisp - 将所有元素增加一个数字
【发布时间】:2019-03-23 21:41:15
【问题描述】:

即使我在函数中增加了它的值,它也会在屏幕上打印 1。有没有办法通过引用调用参数,以便在函数调用后使用它?

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

【问题讨论】:

  • 您有几个问题:1)您将 x 定义为参数,但从未使用它 2)您正在递增 a 但从不存储结果。 stackoverflow.com/questions/3736094/… 应该让你继续前进
  • @blihp 抱歉我更新了它
  • 好的,现在您只需要发布第 2 期即可。请参阅上面的链接,因为我相信它可以解决您的问题。

标签: lisp common-lisp


【解决方案1】:

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

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

注意ax 是不同的变量;如果我们要增加 x,这对 a 没有影响。

数字不是 ANSI Lisp 中的可变对象;如果我们用(incf x) 增加一个变量x,那么会发生(+ 1 x) 的值被计算出来,并将这个新值存储回x 变量中。

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

【讨论】:

    【解决方案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) 在 common lisp 中恰好是 incf,因为经常需要这个增量。所以对于这种非常特殊的情况,你可以使用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 相同。推理此类程序对大脑更友好,因此在许多情况下可以避免出现可避免的错误。顺便说一句,在处理 Lisp 时,FP 绝对是你应该学习的东西。 FP 是您在学习 Lisp 时学到的许多其他概念之一——我不想错过它。

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

    【讨论】:

    • 所以,就问题而言,(defun foo (x) (setf a (+ x 1)))
    • 是的。 (虽然不是闭包;它只是引用了全局特殊环境)。你在回复中写的内容是一个很好的讨论,供初学者学习。 :)
    • 全球环境没有被捕获——它是全球性的,所有人都可以访问。闭包是 let-over-lambda 的结果,其中 captured environment (因此,不仅仅是一个引用)是 not accessible 来自其他任何地方,除了返回的 lambda 函数内部。这就是我对它的理解。 :)
    • 我一直认为(好吧,在理解它之后,或者认为我做到了;))“关闭”是指将环境包含/封闭到 FUNARG 结构中(列表,比如说),至少在概念上是这样。所以,如果没有封闭 - 就只有 没有“闭包”。完全没有。 :)
    • 谢谢!原则上是真的!我可以看到——不管这个词是什么——它会产生很大的不同。 :) 谢谢你的指出!
    猜你喜欢
    • 2017-10-13
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    • 1970-01-01
    • 2017-07-20
    • 2018-04-25
    • 1970-01-01
    • 2011-01-17
    相关资源
    最近更新 更多