LISP gensym 和 Let 函数的问题

Problem with LISP gensym and Let Functions

我正在开发一个程序项目,该程序采用正则表达式,将它们转换为相应的 NFA,然后允许您测试 NFA 是否接受某些输入字符串。

我正在使用函数 gensym 生成对应于 NFA 状态的数字,只是我喜欢使用 *gensym-counter* 只取它的数字部分,一切都是工作正常,但突然间我遇到了这个奇怪的问题:

我用来调用gensym的函数是这个

(defun gensympp ()
  (let ((x (gensym)))
    *gensym-counter*))

所以它 returns 只有在 x 上调用 gensym 后增加的数字,所以我可以将它用于 NFA 状态,今天我是 运行 一些最终测试,突然函数不再增加 *gensym-counter* 所以每个状态都只是起始数字(在函数开始时用 let 设置),解决这个问题的唯一方法似乎是放一个 (defparameter x 1) (数字没关系)在代码的开头,gensym 正常更新并且一切正常一周前,我也是 lisp 的新手,所以可能只是忽略了一些明显的东西,如果需要,我可以 post 我使用 gensym 的其余代码,但甚至只是调用函数 (gensympp) 本身来自听众不会增加数字

非常感谢您的帮助

我猜你的教授想要的是为每个NFA实例重新枚举状态,从0或1开始。也就是说,状态计数器不是全局变量,而是实例变量在某些 NFA 构造上下文对象中。

(如果你考虑一下,这种一致的编号将使你的 NFA 机器更容易被人类阅读,无论你以何种形式转储它们。如果你两次构建相同的 NFA,两者将在图形形状上一致 状态的编号。我见过的所有教科书示例都在每个连续的图表中从 0 或 1 开始对状态进行编号。)

另请注意,您可以为 gensym 提供自定义前缀,它可以是一个空字符串:

[1]> (gensym "")
#:3318

除非为了某些数值计算而实际需要状态为整数,否则它们可以只是符号。

编译器正在优化对 gensym 的调用,因为您从不使用 x。您可以通过将变量作为第二个值返回来欺骗它认为该变量已被使用。

(defun gensympp ()
  (let ((x (gensym)))
    (values *gensym-counter* x)))

你说你不是要使用全局变量:使用系统提供的全局变量,其实就是使用全局变量。

你应该做的是有一些你传递的状态对象,它跟踪当前计数,并让我们根据需要获取下一个计数。

这可以像整数一样简单,但实际上您可能需要可变的东西。有很多方法可以做到这一点。一个简单的是

(defun make-counter (&key (initial-value 0)
                          (increment 1))
  (let ((current (- initial-value increment)))
    (lambda ()
      (incf current increment))))

(defun next-count (counter)
  (funcall counter))

现在:

> (loop with counter = (make-counter :increment 3)
             repeat 4
             collect (next-count counter))
(0 3 6 9)

但是还有很多其他的。

在问题中你说你在绑定*gensym-counter*你自己。好吧,如果那是你正在做的,那么只需使用你自己的特殊变量:它甚至不需要是全局特殊的:

(defmacro with-counter ((&key (initial-value 0)) &body forms)
  `(let ((%the-count% (1- ,initial-value)))
     (declare (special %the-count%))
     ,@forms))

(defun next-count ()
  (declare (special %the-count%))
  (unless (boundp '%the-count%)
    (error "not in dynamic extent of WITH-COUNTER"))
  (incf %the-count%))