【问题标题】:Problem with LISP gensym and Let FunctionsLISP gensym 和 Let 函数的问题
【发布时间】:2020-01-06 19:07:25
【问题描述】:

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

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

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

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

所以它只返回在 x 上调用 gensym 后增加的数字,所以我可以将它用于 NFA 状态,今天我正在运行一些最终测试,突然该函数不再增加 *gensym-counter* 所以每个状态只是起始数字(在函数的开头用 let 设置),解决这个问题的唯一方法似乎是在代码的开头放一个 (defparameter x 1) (数字无关紧要),即 gensym更新正常,一切都像以前一样正常,我真的不知道是什么原因造成的,因为我只对一周前运行良好的代码进行了小的格式调整,我也是 lisp 的新手,所以可能只是很明显我忽略了一些事情,如果需要,我可以发布我使用 gensym 的其余代码,但即使只是从侦听器本身调用函数 (gensympp) 也不会增加数量

非常感谢您的帮助

【问题讨论】:

  • 如果您不使用符号,为什么要使用gensym?只需使用您自己递增的全局整数变量。
  • @Barmar 是的,我的第一个版本只使用了一个全局整数,但我们的教授不希望我们使用全局变量,所以我们必须使用 gensym,我应该提到这一点
  • 您可以在函数周围使用词法变量:(let ((counter 0)) (defun gensympp () (incf counter) counter))
  • 但我想不出*gensym-counter* 没有被递增的原因。只要您在没有参数的情况下调用gensym,它就应该递增。
  • 我想知道编译器是否正在优化对gensym 的调用,因为您从不使用该值,而忽略了它具有副作用的事实。

标签: lisp common-lisp


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

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

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

    除非出于某些数值计算的目的,您实际上需要将状态设为整数,否则它们可能只是符号。

    【讨论】:

      【解决方案3】:

      你说你不是要使用全局变量:使用恰好由系统提供的全局变量,实际上是使用全局变量。

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

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

      (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%))
      

      【讨论】:

        猜你喜欢
        • 2010-10-07
        • 2020-07-12
        • 1970-01-01
        • 2010-10-07
        • 2013-07-09
        • 2015-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多