如何编写优雅的Elisp函数实现驼峰转大写蛇形命名?现有实现待优化
改进方案
现有实现的主要问题
- 递归实现无尾优化,长字符串容易触发栈溢出
- 递归中反复调用
concat,字符串不可变特性导致O(n²)的时间复杂度 - 自定义的
upcasep判断有误:会将数字、符号等非大写字母判定为大写,错误插入下划线(比如isFailed2Upgrade会被转成IS_FAILED_2_UPGRADE) - 未处理连续大写缩写场景(比如
HTTPServer会被转成H_T_T_P_SERVER)
基础优化实现
核心转换逻辑用迭代+字符列表累积,替代原递归实现,效率提升明显,完全符合你给出的示例需求:
(defun change-case-helper (text) "将驼峰风格的TEXT转换为全大写下划线分隔的蛇形格式" (let ((result nil) (len (length text))) (dotimes (i len) (let ((c (aref text i))) ;; 非首字符位置遇到大写字母,前面插入下划线 (when (and (> i 0) (upper-case-p c)) (push ?_ result)) ;; 统一转大写后加入结果列表 (push (upcase c) result))) ;; 逆序列表后一次性拼接为字符串 (concat (nreverse result))))
你原有的主函数change-case无需修改,选中区域/光标取符号的逻辑已经很完善,直接替换上述helper即可运行。
该实现的优势:
- 用列表操作替代多次
concat,时间复杂度从O(n²)降到O(n) - 用Emacs内置的
upper-case-p判断大写,自动兼容多字节字符,无自定义判断的边界bug - 仅在非首字符的大写前插入下划线,避免开头多余的冗余下划线
进阶适配缩写场景
如果需要处理连续大写的缩写场景(比如HTTPServer转HTTP_SERVER、ParseJSONString转PARSE_JSON_STRING),可以调整判断条件,仅当大写字母的前一位是小写、或后一位是小写时才插入下划线:
(defun change-case-helper (text) "转换驼峰为大写下划线蛇形,兼容连续大写缩写" (let ((result nil) (len (length text))) (dotimes (i len) (let* ((c (aref text i)) (prev-c (if (> i 0) (aref text (1- i)) nil)) (next-c (if (< i (1- len)) (aref text (1+ i)) nil))) ;; 非首字符的大写,满足前一位是小写/后一位是小写时,前插下划线 (when (and (> i 0) (upper-case-p c) (or (lower-case-p prev-c) (lower-case-p next-c))) (push ?_ result)) (push (upcase c) result))) (concat (nreverse result))))
该版本对常见驼峰命名的转换效果更符合实际使用习惯,不会把连续的大写缩写拆分成单个字符分隔的格式。
内容的提问来源于stack exchange,提问作者fast tooth
相关产品推荐
相关产品推荐

