SBCL报“STR函数未定义”错误:字符串去重空格问题求助
环境:Windows 11,PowerShell中运行Steel Bank Common Lisp(SBCL)2.0.0版本的REPL
我编写了一个用于去除字符串多余空格的函数(目标是将"This is a string"转换为"This is a string"),代码如下:
(defun trim-spaces (str) (let* ((len (length(str))) (i 1) (last-char (char str (- 1 i))) (curr-char (char str i)) ) (dotimes (i len) (cond ((eql last-char #\Space) (cond ((eql curr-char #\Space) (setq str (remove curr-char str))))))))) (trim-spaces "This is a string")
此前编写的类似功能函数(如去除字符串括号的函数)均可正常运行,但执行上述代码时,无论传入字符串字面量还是变量,都会触发以下错误:
debugger invoked on a UNDEFINED-FUNCTION @1000000C2C in thread
#<THREAD tid=19952 "main thread" RUNNING {1100BC8003}>:
The function COMMON-LISP-USER::STR is undefined.
想了解该问题的成因,以及对应的解决或规避方法。
问题成因
当前错误的直接原因是语法错误:Common Lisp的函数调用语法要求函数名与参数之间必须用空格分隔,你写的(length(str))会被解析为「调用名为STR的函数,并把length作为它的参数」,但你并没有定义过STR函数,因此触发未定义函数错误。
除此之外,你的函数还存在逻辑缺陷:
let*中初始化的i=1会被dotimes重新绑定的局部变量i覆盖,导致初始定义的last-char和curr-char只会取字符串固定位置的字符,不会随循环更新;(remove curr-char str)会删除字符串中所有空格,而非仅连续的多余空格,无法实现预期功能。
解决方法
1. 修复语法错误(解决当前报错)
将(length(str))修改为(length str),严格遵循Common Lisp函数调用的语法规范。
2. 修正函数逻辑(实现预期功能)
以下提供两种可行的实现方式:
方式一:原生Common Lisp实现(无第三方依赖)
手动遍历字符串,构建仅保留单个连续空格的结果:
(defun trim-spaces (str) (with-output-to-string (out) (let ((last-space-p nil)) (loop for char across str do (cond ((char= char #\Space) (unless last-space-p (write-char char out) (setf last-space-p t))) (t (write-char char out) (setf last-space-p nil)))))))
逻辑说明:
- 用
with-output-to-string创建字符串输出流,便捷构建结果; - 用
last-space-p标记上一个字符是否为空格; - 遍历每个字符:若为空格,仅在上一个字符不是空格时写入;若非空格字符,直接写入并重置标记。
测试验证:
(trim-spaces "This is a string") ; 返回 "This is a string"
方式二:使用正则表达式(依赖第三方库cl-ppcre)
若允许使用第三方库,cl-ppcre的正则替换功能可以更简洁地实现需求:
(ql:quickload :cl-ppcre) ; 先加载库 (defun trim-spaces (str) (cl-ppcre:regex-replace-all "\\s+" str " "))
规避方法
- 编写Common Lisp代码时,严格遵守函数调用语法:函数名与参数列表之间必须用空格分隔,不要沿用C语言风格的
函数名(参数)写法; - 调试时优先查看错误提示的第一行内容,通常会直接指出未定义函数或语法错误的位置,快速定位问题。
内容的提问来源于stack exchange,提问作者Ashley Ben Story

