在SBCL中运行DO循环时出现COMMON-LISP:PROGN未定义变量错误
Common Lisp中PROGN未绑定错误的排查
问题场景
在MacOS Sonoma 14.2.1系统上使用SBCL 2.4.1版本编写了以下Common Lisp函数,运行时出现PROGN未绑定的错误。此前在GNU CLISP中多次正常使用该写法,且程序其他部分使用PROGN也无问题。
原函数代码
(defun getEndLoop(n) (let ((lpCntThrshld 8192)) ; To be safe and avoid any possible infinite loop. (do ((count 0 (+ count 1)) (token n (collatzNext token PrimeList)) ; tail implements a mechanism to detect loops in the flow of values. (tail n (if (and (eq stage 0) (eq (mod count 2) 1)) (collatzNext tail PrimeList) tail)) (stage 0 (if (and (> count 0) (eql token tail)) 1 stage))) (progn (if (> count lpCntThrshld) (return-from getEndLoop (list -1 fnID n))) (if (eq stage 1) ;(and (> count 0) (eql token tail)) (progn (format t "stage=~a" stage) (return-from getEndLoop NIL) ) )) ) ; End do. ) ; End getEndLoop-let. ) ; End getEndLoop.
错误信息
; caught WARNING: ; undefined variable: COMMON-LISP:PROGN ; ; compilation unit finished ; Undefined variable: ; PROGN ; caught 1 WARNING condition Unhandled UNBOUND-VARIABLE in thread #<SB-THREAD:THREAD "main thread" RUNNING {7005550003}>: The variable PROGN is unbound.
错误原因
问题出在DO循环的语法误用:
Common Lisp标准中DO的语法结构是:
(DO (variable-clause*) (end-test-form result-form*) declaration* form*)
你错误地将原本属于**循环体(form*)的代码用PROGN包裹后,放在了结束测试(end-test-form)**的位置(也就是DO的第二个参数位置)。SBCL严格遵循标准,将该位置的PROGN视为普通变量而非特殊运算符,因此抛出“未绑定变量”的错误。CLISP对语法的宽容度更高,允许这种非标准写法,所以之前在CLISP中能正常运行。
修正后的代码
调整DO的结构,将结束条件与返回值逻辑放在第二个参数区域,循环体按需放在第三个参数区域:
(defun getEndLoop(n) (let ((lpCntThrshld 8192)) ; To be safe and avoid any possible infinite loop. (do ((count 0 (+ count 1)) (token n (collatzNext token PrimeList)) ; tail implements a mechanism to detect loops in the flow of values. (tail n (if (and (eq stage 0) (eq (mod count 2) 1)) (collatzNext tail PrimeList) tail)) (stage 0 (if (and (> count 0) (eql token tail)) 1 stage))) ;; 结束测试:满足任一条件则退出循环 ((or (> count lpCntThrshld) (eq stage 1)) ;; 退出时的返回值逻辑 (cond ((> count lpCntThrshld) (list -1 fnID n)) ((eq stage 1) (format t "stage=~a" stage) NIL))) ;; 循环体:每次迭代执行的额外操作(此处无需额外代码,逻辑已整合到结束测试) ) ; End do. ) ; End getEndLoop-let. ) ; End getEndLoop.
内容的提问来源于stack exchange,提问作者Michel
相关产品推荐
相关产品推荐

