代码之家  ›  专栏  ›  技术社区  ›  Tamas Czinege

lisp中的变量引用

  •  24
  • Tamas Czinege  · 技术社区  · 17 年前

    (defun increase-by-one (var)
      (setf var (+ var 1)))
    

    现在很明显,问题是这个函数只增加堆栈上变量副本的值,而不是实际的原始变量。我也尝试过通过使用宏来实现效果,但没有取得多大成功,尽管我觉得使用宏是正确的方法。

    我在LISP中总是碰到这堵墙,我确信一定有办法绕过它,或者在LISP中有一种完全不同的方法来解决这个问题,我没有想过?这样的事情是如何在LISP中完成的?

    编辑 :多人建议使用 incf . 我只是用这个例子简单地演示了这个问题,实际上我并不是在寻找重新实现incf的方法。不过还是谢谢你的建议。

    6 回复  |  直到 17 年前
        1
  •  25
  •   Rainer Joswig mmmmmm    8 年前

    对于词法范围,用户无法访问不在当前范围内的变量。也不能将词法变量直接传递给其他函数。Lisp计算变量并传递绑定到这些变量的值。没有什么比对变量的一级引用更好的了。

    想想功能!

    (let ((a 1))
      (values (lambda (new-value)
                (setf a new-value)) 
              (lambda () a)))
    

    上面返回两个函数。一个可以读取变量,另一个可以写入变量。

    writer 第二个呢 reader .

    (defun increase-by-one (writer reader)
       (funcall writer (1+ (funcall reader))))
    

    因此,要执行您想要的操作,代码需要a)位于作用域中,或者b)可以访问作用域中的函数。

    变量也可以是全局变量

    (defvar *counter* 1)
    
    (defun increase-by-one (symbol)
      (set symbol (1+ (symbol-value symbol))))
      ; note the use of SET to set a symbol value
    
    (increase-by-one '*counter*)
    

    这适用于由符号表示的全局变量。它不适用于词汇变量-这些变量不由符号表示。

    还有一个宏 INCF 增加“位置”(例如变量)。

    (incf a)
    

    但是 a

    (defun foo (a)
      (incf a))  ; increases the local variable a
    

    这里可以看到极限 :

    (defun foo (var)
      (add-one-some-how var))
    
    (let ((a 1))
       (foo something-referencing-a))
    

    A. FOO .

    ,以便调用提供的函数。

    (defun foo (f)
      (funcall f 1))   ; calls the function with 1
    
    (let ((a 1))
       (foo (lambda (n)
              (setf a (+ a n)))))
       ;; passes a function to foo that can set a
    
        2
  •  11
  •   dmitry_vk    17 年前

    (defstruct reference getter setter)
    
    (defmacro ref (place)
      (let ((new-value (gensym)))
        `(make-reference :getter (lambda () ,place)
                         :setter (lambda (,new-value)
                                   (setf ,place ,new-value)))))
    
    (defun dereference (reference)
      (funcall (reference-getter reference)))
    
    (defun (setf dereference) (new-value reference)
      (funcall (reference-setter reference) new-value))
    

    然后你可以使用它:

    (defun increase-by-one (var-ref)
      (incf (dereference var-ref)))
    
    (defun test-inc-by-one (n)
      (let ((m n))
        (increase-by-one (ref m))
        (values m n)))
    
    (test-inc-by-one 10) => 11, 10
    
        3
  •  10
  •   Greg Hewgill    17 年前

    而公共口齿不清 支持 一种函数式编程风格,这不是它的主要关注点(Scheme虽然不是纯函数式的,但更接近)。CommonLisp非常好地支持完全命令式的编程风格。

    (defmacro increase-by-one (var)
      `(setf ,var (+ ,var 1)))
    

    这允许您编写如下代码:

    (increase-by-one foo)
    

    将扩展为:

    (setf foo (+ foo 1))
    

    在编译之前。

        4
  •  1
  •   Ken    17 年前

    宏可能是您想要的,因为它们不计算参数,所以如果您传递变量名,则得到的是变量名,而不是它的值。

    编辑:我建议INCF不是作为编写自己的代码的替代品,而是因为它做你想要的,并且是一个宏,因此你可以很容易地找到它的源代码,例如。, ABCL CMUCL

        5
  •  0
  •   anon anon    17 年前

    我认为你错过了函数式编程的一个关键概念——你不应该在对象被创建后改变它们的状态。通过引用更改某些内容违反了这一点。

        6
  •  0
  •   cjohansson    6 年前

    通过使用返回值可以产生类似的效果,这在Lisp中是一种可能的设计模式,即使它不是通过引用传递变量。

    这样地:

    (defun increase (subject increment)
      (+ subject increment))
    
    (let ((my-var 0))
      (message "Initial value: %s" my-var)
      (setq my-var (increase my-var 25))
      (message "Final value: %s" my-var))
    
        7
  •  -3
  •   user1079212    14 年前

    SBCL

    (defmacro inc-by-num (var num)
               (set var (+ (eval var) num)))
    

    setf b/c它限制了范围,而 set 不。此外,您可能需要使用 eval 之前 var 如果出现“参数X不是数字”错误。