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

Lisp流读取解析单词时出现意外无限循环问题求助

问题详情

我正在尝试从stream中读取“单词”,单词分为三类:纯数字如“1234”、常规字母组成的单词如“test”、带撇号的缩写词如“you're”。
实现该需求的方法有很多,我打算用tagbody实现类似状态机的逻辑来解析单词。输入符合规范时代码可以正常运行,但输入不符合单词格式时就会出错。我本想跳过无效输入直到遇到空白符或EOF,结果触发了无限循环,不清楚问题原因。
以下是原始代码:

(defun whitespace-or-nil-p (c)
  "Returns if a character is whitespace"
  (member c '(#\  #\Tab #\Return #\Newline nil)))

(defun read-word (stream)
  (let ((c nil))
    (with-output-to-string (out)
      (tagbody
       read-initial
         (setf c (read-char stream nil))
         (cond
           ((whitespace-or-nil-p c) (peek-char t stream nil) (go read-initial))
           ((not (alphanumericp c)) (go skip-til-next))
           ((digit-char-p c) (go read-number))
           ((alpha-char-p c) (go read-simple-or-contracted-word))
           (t (return-from read-word))
           )
       skip-til-next
         (get-output-stream-string out)
         (loop until (whitespace-or-nil-p (peek-char nil stream nil)) do (read-char stream nil))
         (go read-initial)
       read-number
         (write-char c out)
         (setf c (read-char stream nil))
         (cond
           ((whitespace-or-nil-p c) 
              (return-from read-word (get-output-stream-string out)))
           ((not (digit-char-p c)) (go skip-til-next))
           (t (go read-number))
           )
       read-simple-or-contracted-word
         (write-char c out)
         (setf c (read-char stream nil))
         (cond
           ((whitespace-or-nil-p c) 
              (return-from read-word (get-output-stream-string out)))
           ((and (char/= c #\') (not (alpha-char-p c))) (go skip-til-next))
           (t (go read-simple-or-contracted-word))
           )
         ))))

问题原因与修复方案

核心问题

无限循环的触发点在read-initial状态的EOF处理逻辑:当流读取到末尾时,read-char返回nil,会命中第一个cond分支,执行peek-char t stream nil后再次跳回read-initial,反复读取EOF永远不会退出,形成死循环。

其他潜在问题

  • read-initial状态中的兜底返回分支永远无法命中,属于无效死代码,因为前面的条件已经覆盖了所有可能的字符类型。
  • skip-til-next状态中调用get-output-stream-string out会清空已经写入输出流的有效内容,属于多余操作。
  • read-initial状态中额外调用peek-char t没有必要,反而可能引入不可预期的流指针偏移。

修复后代码

(defun whitespace-or-nil-p (c)
  "Returns if a character is whitespace"
  (member c '(#\  #\Tab #\Return #\Newline nil)))

(defun read-word (stream)
  (let ((c nil))
    (with-output-to-string (out)
      (tagbody
       read-initial
         (setf c (read-char stream nil))
         (cond
           ;; 读到EOF直接终止返回
           ((null c) (return-from read-word nil))
           ;; 跳过空白字符直接重读
           ((whitespace-or-nil-p c) (go read-initial))
           ((not (alphanumericp c)) (go skip-til-next))
           ((digit-char-p c) (go read-number))
           ((alpha-char-p c) (go read-simple-or-contracted-word)))
       skip-til-next
         ;; 清空无效缓存内容
         (get-output-stream-string out)
         (loop
           (let ((next-c (peek-char nil stream nil)))
             (when (whitespace-or-nil-p next-c)
               (return))
             (read-char stream nil)))
         (go read-initial)
       read-number
         (write-char c out)
         (setf c (read-char stream nil))
         (cond
           ((whitespace-or-nil-p c) 
              (return-from read-word (get-output-stream-string out)))
           ((not (digit-char-p c)) (go skip-til-next))
           (t (go read-number)))
       read-simple-or-contracted-word
         (write-char c out)
         (setf c (read-char stream nil))
         (cond
           ((whitespace-or-nil-p c) 
              (return-from read-word (get-output-stream-string out)))
           ((and (char/= c #\') (not (alpha-char-p c))) (go skip-til-next))
           (t (go read-simple-or-contracted-word)))))))

修复说明

  1. 新增EOF优先判断逻辑,读到流末尾直接返回nil终止函数,彻底解决无限循环问题。
  2. 移除read-initial状态中多余的peek-char t调用,避免不必要的流操作。
  3. 优化skip-til-next的循环逻辑,显式判断peek结果,降低隐式状态异常风险。
  4. 移除read-initial状态中无效的兜底返回分支。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 23:09:01