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

时间:2013-12-03 14:33:08

标签: 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)))

在最后一篇文章中,我必须eval global,因为它引用了symbol-value然后我copy-list,因此global isn' t由我选择的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)

...通过访问函数*g1**g3*set-global等可以使用相同的功能。

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

2 个答案:

答案 0 :(得分:6)

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

  1. push-something相当于cons

  2. push-cdr 功能:它会就地修改参数或失败,并在nil参数上显示错误。

  3. 如果你想改变全局变量的值,那么明确地使用setf会更清楚。

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

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

    PS。我不确定你的意思" setf在函数"中没有改变全局变量的值,但是:

    (defvar *foo* 10)
    *FOO* ==> 10
    (defun change-foo (x) (setq *foo* x))
    (change-foo 123)
    *foo* ==> 123
    

答案 1 :(得分:-2)

为什么不完全摆脱set-global函数,如果你希望你的所有函数都具有相同的传递全局变量的风格,那么只需使它们全部使用symbol-value所以任何人使用这些函数将不需要知道在全局变量名称前面传递哪个以及哪些不需要引用。更容易吧?