我很难理解一些具有更多经验的人可能理解的代码的工作原理:
(let ((x 0))
(loop for var in '(x)
do (set var 3))
x)
Run Code Online (Sandbox Code Playgroud)
我的期望是表达式应该返回3,x的新值,但它实际上返回0.为什么x没有被重置?是否有另一种方法来迭代变量列表并为它们赋值?
经过一些试验和错误,我设法编写宏mashup来打印几个变量的名称和值(用于调试).它似乎有效,但我想看看如何更"专业"地编码?
(defmacro prt1 (var)
;Print a single variable and its value.
`(progn (princ (symbol-name ',var)) (princ " = ") (princ ,var) (terpri)))
(defmacro prt (&rest vars)
;Print the values of a number of variable names.
(eval `(append (list 'progn)
(map 'list #'(lambda (x) (list 'prt1 x)) ',vars)
(list (list 'terpri))
(list t)))) ;need to return t
Run Code Online (Sandbox Code Playgroud)
(prt A B C)然后调用打印当前绑定 - 例如:
A = 1
B = 2
C = 3
T
Run Code Online (Sandbox Code Playgroud) 这个问题是使用局部特殊变量的注释的继续,关于如何最好地避免全局变量。据我了解,全局变量存在问题,主要是因为它们有可能干扰参照透明性。如果表达式使用其调用上下文之外的信息来更改全局值(例如,全局变量本身的先前值或任何其他外部值),则会违反透明度。在这些情况下,评估表达式在不同时间可能有不同的结果,无论是返回的值还是副作用。(但是,似乎并不是所有的全局更新都是有问题的,因为某些更新可能不依赖于任何外部信息,例如,将全局计数器重置为0)。深度嵌入计数器的常规全局方法可能类似于:
* (defparameter *x* 0)
*X*
* (defun foo ()
(incf *x*))
FOO
* (defun bar ()
(foo))
BAR
* (bar)
1
* *x*
1
Run Code Online (Sandbox Code Playgroud)
这似乎违反了参照透明性,因为它(incf *x*)依赖于外部(全局)值*x*来执行其工作。以下是通过消除全局变量来维持功能和引用透明性的尝试,但是我不相信它确实可以做到:
* (let ((x 0))
(defun inc-x () (incf x))
(defun reset-x () (setf x 0))
(defun get-x () x))
GET-X
* (defun bar ()
(inc-x))
BAR
* (defun foo ()
(bar))
FOO
* (get-x)
0
* (foo)
1
* (get-x)
1
Run Code Online (Sandbox Code Playgroud)
全局变量现在已经消失了,但是表达式似乎仍然(inc-x)具有(潜在的)副作用,并且每次调用它都会返回不同的(但未使用的)值。这是否确认对有关变量使用闭包不能解决透明性问题?
我希望这不会打败您,但我想对编写引用透明代码的另一种可能策略提出意见。(先前有关引用透明性的讨论在“ 使用闭包而不是全局变量”中)。再次,目标是消除大多数全局变量,但保留其便利性,而无需在代码中注入容易产生错误的引用或潜在的非功能性行为(即,参照不透明,副作用和不可重复的评估)。
建议使用局部特殊变量建立初始绑定,然后可以将其动态传递给最终使用它们的后续嵌套函数。像全局变量一样,其预期的优点是不需要将局部特殊变量作为参数传递给所有中间函数(其功能与局部特殊变量无关)。但是,为了保持引用透明性,它们将作为参数传递给最终使用者函数。
我想知道的是,是否在浮动很多动态变量时容易产生编程错误。我似乎不太容易出错,因为任何先前绑定的变量的本地重新绑定在释放后都不会影响原始绑定:
(defun main ()
(let ((x 0))
(declare (special x))
(fum)))
(defun fum ()
(let ((x 1)) ;inadvertant? use of x
(setf x 2))
(foo))
(defun foo ()
(declare (special x))
(bar x))
(defun bar (arg) ;final consumer of x
arg)
(main) => 0
Run Code Online (Sandbox Code Playgroud)
这种策略有问题吗?