在函数内重新分配全局变量而不传递变量名称

时间:2018-05-21 23:55:05

标签: binding lisp common-lisp

如标题所述,我尝试解决以下问题:

(setq s ())

(defun reassign (val)
  (setq val 10))

(reassign s)

(print s)
-> Output: s = nil

我希望reassign破坏性地分配s = 10。 有没有一种简单的方法来处理这个问题,而不使用函数&中的变量名s。只改变功能? (可能包括宏吗?)我将不胜感激任何建议! :)

2 个答案:

答案 0 :(得分:2)

(defun reassign (val)
  (setq val 10))

(reassign s)

当您致电(reassign s)时,s的值会传递到reassign,并在val本地绑定到nil。在您的情况下,这将是s,假设您已将()DEFVAR一起声明,然后再将其设置为reassign(请参阅setq and defvar in Lisp)。

(setf (symbol-value 's) 10) 内,对SETQ的调用会更改本地绑定。

全局绑定

每个符号都可以包含全局值。 如果要更改符号的值单元格,请使用SYMBOL-VALUE访问者:

s

注意s是如何引用的。您没有更改当前绑定到nil的符号(s,一个常量变量),但(SETF SYMBOL)符号本身。 (defmacro reassign (place) `(setf ,place 10)) 就像直接致电SET

词汇和动态绑定

但是,如果要修改任何类型的 place ,特别是词法和动态变量绑定,则需要定义一个宏:

(gethash key table)

SETF扩展为更新地点所需的代码。你也可以给reassign而不只是一个不带引号的变量,然后更新表的内容。

对于局部变量,调用 {{1}}的代码最终会扩展为对特殊运算符SETQ的调用,该运算符知道如何更改词法绑定(它还处理SYMBOL-MACROLET绑定)。

答案 1 :(得分:1)

是的,你猜错了。你需要一个宏。

(defmacro reassign (var &optional (val 10)) `(setq ,var ,val))

(defvar s ())   ;; @Svante remarked that setq can be dangerous to create a variable
s ;; NIL
(reassign s)
s ;; 10

;; the `&optional (val 10)` makes the value optional and 10 to the 
;; default value of val if not given.

在你的例子中

(defun reassign (val) (setq val 10))

valsetq特殊形式的第一个参数:val中的setq未成为s,尽管您已经reassign { {1}} val。它保持val,以便您将功能范围中的10分配给val。你可以通过以下方式看到它:

(setq s ())

(defun reassign (val)
  (print "You gave for `val`:")
  (print val) 
  (setq val 10)
  (print "The form assigned 10 literally to val")
  (print "Proof: Print out val in this scope:")
  (print val))

(reassign s)

;; 
;; "You gave for `val`:" 
;; NIL 
;; "The form assigned 10 literally to val" 
;; "Proof: Print out val in this scope:" 
;; 10 
;; 10

(print val)

*** - SYSTEM::READ-EVAL-PRINT: variable VAL has no value
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead of VAL.
STORE-VALUE    :R2      Input a new value for VAL.
ABORT          :R3      Abort main loop
;; since val has not been initiated before

此外,函数的setq在功能范围内执行。这就是为什么在函数val之外保留旧值。

(defvar val ()) ;; NIL
(print val)   ;; NIL

(defun reassign (val)
  (print "You gave for `val`:")
  (print val) 
  (setq val 10)
  (print "The form assigned 10 literally to val")
  (print "Proof: Print out val in this scope:")
  (print val))

(reassign 'whatever) ;; inside function val becomes 10
;;
;;"You gave for `val`:" 
;;WHATEVER 
;;"The form assigned 10 literally to val" 
;;"Proof: Print out val in this scope:" 
;;10 ;; val becomes indeed 10 until leaving function 
;;10

;; but retains old value after leaving the function
(print val)
;;
;; NIL
;; NIL

相反,宏以(reassign s)形式替换宏调用(setq s 10)。因此,在执行时,重新分配发生在我们定义(defvar s ())的环境中。 因此,在重新分配后,s会保留新分配的值,因为它已在工作环境中重新分配。