【问题标题】:Reassigning a global variable within a function without passing variable name在函数中重新分配全局变量而不传递变量名
【发布时间】:2018-10-31 15:33:54
【问题描述】:

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

(setq s ())

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

(reassign s)

(print s)
-> Output: s = nil

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

【问题讨论】:

标签: binding lisp common-lisp


【解决方案1】:
(defun reassign (val)
  (setq val 10))

(reassign s)

当您调用(reassign s) 时,s 的值被传递给reassign,它在本地绑定到val。假设您在将 DEFVAR 设置为 () 之前已经声明了 sDEFVAR,那么在您的情况下就是 nil(请参阅 setq and defvar in Lisp)。

reassign 内部,对SETQ 的调用会更改本地绑定。

全局绑定

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

(setf (symbol-value 's) 10)

注意s 是如何被引用的。您没有更改当前绑定到s 的符号(这将是nil,一个常量变量),而是s 符号本身。 (SETF SYMBOL) 相当于直接调用SET

词法和动态绑定

但是,如果您想修改任何类型的位置,尤其是词法和动态变量绑定,则需要定义一个宏:

(defmacro reassign (place)
  `(setf ,place 10))

SETF 扩展为更新地点所需的代码。您还可以提供 (gethash key table) 而不仅仅是一个未加引号的变量,然后它会更新表格的内容。

在局部变量的情况下,调用reassign的代码最终会扩展为对特殊运算符SETQ的调用,它知道如何更改词法绑定(它还处理@ 987654328@ 绑定)。

【讨论】:

    【解决方案2】:

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

    (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 特殊形式的第一个参数:setq 中的 val 不会变成 s,尽管您将 reassign 指定为 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 会保留新分配的值,因为它是在工作环境中重新分配的。

    【讨论】:

    • Setq 不是创建变量的正确运算符。使用defvardefparameterletmultiple-value-binddo
    • 谢谢你,斯万特。我还要学很多东西——你是我的老师!
    • 谢谢你们两个:)
    • 欢迎您,Kisslax! :)
    猜你喜欢
    • 2019-10-21
    • 1970-01-01
    • 1970-01-01
    • 2021-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    相关资源
    最近更新 更多