在Lisp中的变量引用(Variable references in lisp)

2019-06-18 05:52发布

另一个新手(公用)LISP的问题:

基本上大多数编程语言有一个均值函数接受引用变量,而不是仅仅值,即,通过引用传递,而不是按值传递。 比方说,为简单起见,我想写一个接收一个变量,并通过一个增加了变量的值LISP功能:

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

现在很明显的问题是,这一功能不仅增加了变量的堆栈,而不是实际的原始变量的副本的价值。 我也试图通过使用宏没有多少成功达到的效果,虽然我有这种感觉,使用宏是正确的道路要走。

我打这堵墙所有的时间在LISP,我敢肯定一定有办法解决它或许有一个完全不同的方法来在LISP这个问题我还没有想过? 如何像这样的事情在做LISP?

编辑 :多的人已经使用建议incf 。 我只是用这个例子来证明一个简单的方法的问题,我是不是实际上是寻找重新实现INCF。 但感谢反正建议。

Answer 1:

随着词汇范围,一个没有访问到不是在当前范围内的变量。 你也不能直接通过词法变量的其他功能。 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))))

所以,做你想做的,代码需要一个)是在范围或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))

有没有办法通过直接引用aFOO

唯一的办法是提供一个功能。 我们也不得不重写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


Answer 2:

虽然Common Lisp的支持功能的编程风格,这是不是它的一般重点(方案,而不是单纯的功能性,更接近)。 Common Lisp的支持编程的完全必要的风格非常好。

如果你发现你需要写这样的代码,通常的解决方法是一个宏:

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

这使您可以编写代码,如:

(increase-by-one foo)

这将扩大到:

(setf foo (+ foo 1))

之前它被编译。



Answer 3:

当然,在Lisp中你可以让你自己的方式使变量引用,如果你想。 最简单的方法是这样的:

(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


Answer 4:

宏可能是你想要的,因为他们不评估它们的参数,所以如果你传递一个变量名,你得到一个变量的名称,而不是它的价值。

INCF不正是你想要的东西,所以如果你谷歌“defmacro INCF”你会发现一大堆的这个定义,其中一些甚至接近是正确。 :-)

编辑:我是在暗示INCF不作为替代写你自己的,但因为你想要做什么,并且是一个宏观的,所以你可以很容易地找到它的源代码,例如, ABCL或CMUCL 。



Answer 5:

我想你是对函数式编程的关键概念之一错过了 - 你不应该改变对象的状态一旦被创建。 通过参考变化的东西违反了。



Answer 6:

作为一个初学者我来这里是要弄清楚怎么做应该是在任何语言中一个微不足道的过程。 最上面贴的解决方案,没有正常工作,可能比需要的是什么,或者不同的实现更加复杂。 下面是SBCL一个简单的解决方案:

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

显然,你不能使用setf B / C它,而将范围限定为set则没有。 您可能还需要使用eval之前var如果你得到一个“说法X不是一个数字”的错误。



文章来源: Variable references in lisp