创建一个函数库来改变任意全局变量

joh*_*ers 0 lisp common-lisp

更新:如果我有一些函数需要改变作为函数参数传递的全局变量,有些函数可以通过传递全局符号来实现(就像将某些东西推到列表的一部分一样) ,但是其他函数将要求在传递符号时引用符号,这样我就可以访问符号值并改变外部变量,这是在函数中使用setq所必需的.这个问题基本上是为这些函数的用户创建一致的界面,而不会混淆它们,有时你传递符号,othertimes你传递引用的符号

通过使用具有相同用法的帮助程序来简化一堆函数的接口的示例方法,无论变异函数如何工作:

以下是我的技术的两个基本示例:

(defun push-something (global something)
  (push something global)
  global) ;; not necessary to return it here since the 
          ;; preceding line already does, but doing so 
          ;; for consistency with other functions and clarity

(defun push-cdr (global something)
  (push something (cdr global))
  global) ;; must return it if I am going to set the 
          ;; dynamic var to the result of this function, 
          ;; because the preceding line doesn't return the 
          ;; entire new result of the variable

;;; "dirty" function for mutating global variable using "clean" functions above:

(defun set-global (fn global &rest values)
  (setf (symbol-value global) (apply fn (copy-list (eval global)) values)))
Run Code Online (Sandbox Code Playgroud)

在这最后一个,我要evalglobal,因为它是引用与使用symbol-value,然后我copy-listglobal不直接不慎被任何突变fn我选择(因为会出现这种情况有push-cdr可能是在更复杂的功能不平凡的).

输出:

CL-USER> (defparameter *g2* nil)
*G2*
CL-USER> (set-global #'push-something '*g2* 5)
(5)
CL-USER> *g2*
(5)
CL-USER> (set-global #'push-cdr '*g2* 99)
(5 99)
CL-USER> *g2*
(5 99)
Run Code Online (Sandbox Code Playgroud)

...和相同的功能可以与使用*g1**g3*等全部通过接入功能set-global.

这是好的还是坏的风格,还是我错过了更好的方法来实现这个功能?

sds*_*sds 6

这是一种糟糕的风格,浪费你的时间.

  1. push-something相当于cons.

  2. push-cdr实用的:它修改就地参数或失败,对一个错误nil的参数.

如果要更改全局变量的值,则setf显式使用更清晰.

如果要以某种复杂的方式修改全局值的内部,可能需要为此编写一个特殊函数 - 但该函数应该不知道它正在修改绑定到全局变量的值而不是一个词汇变量.

简而言之,有两个独立的正交问题 - 改变一个全局变量(使用setq)和改变一个对象的内部(不可知它的全局性).

PS.我不确定你的意思是"setf在函数内部时不会改变全局变量的值",但是:

(defvar *foo* 10)
*FOO* ==> 10
(defun change-foo (x) (setq *foo* x))
(change-foo 123)
*foo* ==> 123
Run Code Online (Sandbox Code Playgroud)