You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SBCL中CCASE与CASE、ECASE的异常行为问题

Ecase与Ccase在SBCL 2.0.1中的行为差异解析

在SBCL 2.0.1版本中,使用ecase处理符号'dos时运行正常,但用ccase处理同一个符号会触发编译警告,查阅CLTL2规范未找到直接解释,核心原因在于两个宏的设计目标和参数要求存在本质区别。

代码示例

Ecase 运行结果

(ecase 'dos
  ((i uno) 1)
  ((ii dos) 2)
  ((iii tres) 3))

; 输出:2

Ccase 运行警告与结果

(ccase 'dos
  ((i uno) 1)
  ((ii dos) 2)
  ((iii tres) 3))

; 编译警告信息:
; in: CCASE 'DOS
;     (SETF 'DOS
;             (SB-KERNEL:CASE-BODY-ERROR 'CCASE ''DOS #:G680
;                                        '(MEMBER I UNO II DOS III TRES)
;                                        '(I UNO II DOS III TRES)))
; --> LET* FUNCALL 
; ==>
;   (SB-C::%FUNCALL #'(SETF QUOTE) #:NEW1 #:DOS3)
; 
; caught WARNING:
;   The function (SETF QUOTE) is undefined, and its name is reserved by ANSI CL so
;   that even if it were defined later, the code doing so would not be portable.

; ==>
;   (LET* ((#:DOS3 DOS)
;          (#:NEW1
;           (SB-KERNEL:CASE-BODY-ERROR 'CCASE ''DOS #:G680
;                                      '(MEMBER I UNO II DOS III TRES)
;                                      '(I UNO II DOS III TRES))))
;     (FUNCALL #'(SETF QUOTE) #:NEW1 #:DOS3))
; 
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::DOS
; 
; compilation unit finished
;   Undefined function:
;     (SETF QUOTE)
;   Undefined variable:
;     DOS
;   caught 2 WARNING conditions

; 输出:2

差异原因解析

  1. 宏的核心设计区别

    • ecase的第一个参数是普通表达式:它只需要计算表达式的值,然后匹配分支,不需要对输入做任何修改。所以'dos作为常量表达式完全合法,直接取值匹配即可。
    • ccase的第一个参数是可修改位置(place):这个宏的设计目的是,当匹配失败时,允许通过用户提供的错误处理函数修改该位置的值,然后重新尝试匹配。因此它要求第一个参数必须是能被setf修改的合法位置,比如变量、数组元素、对象槽位等。
  2. 警告的具体由来

    • 当给ccase传入'dos时,SBCL会尝试生成代码去修改这个"位置",但'dos是quote的结果,属于常量,根本不能被赋值。编译器把这个操作解析为(setf 'dos ...),这相当于试图调用(setf quote)函数——而ANSI Common Lisp明确保留了(setf quote)的名字,不允许定义该函数,因此触发第一个警告。
    • 第二个警告是因为编译器错误地将'dos展开为对变量DOS的引用,但这个变量从未被定义过,所以提示未定义变量。
  3. 正确使用场景

    • 如果只需要根据值匹配分支,不需要修改输入,用ecase即可。
    • 如果需要在匹配失败时修改输入位置的值(比如让用户输入新值后重试),才使用ccase,此时第一个参数必须是合法的可赋值位置,比如:
      (let ((x 'dos))
        (ccase x
          ((i uno) 1)
          ((ii dos) 2)
          ((iii tres) 3)))
      
      这段代码就不会触发警告,因为x是可修改的变量。

内容的提问来源于stack exchange,提问作者John Badie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 06:23:17