在Common Lisp中为PLIST键应用类正则模式匹配的实现问询
没问题,这个需求在Common Lisp里很好实现!虽然原生没有直接支持正则匹配关键字的语法,但我们可以通过关键字转字符串+前缀匹配的方式来模拟:input*或:output*的效果,再结合cond或者直接的条件判断来完成逻辑。
先给你一个完整的实现示例,然后一步步解释:
;; 先实现一个通用的前缀匹配函数(如果你的Lisp实现没有原生的string-starts-with-p) (defun string-starts-with-p (str prefix) (and (>= (length str) (length prefix)) (string= str prefix :end1 (length prefix)))) (defun extract-expand-and-io (plist) (cond ;; 第一个分支:如果没有:expand键,直接返回nil ((not (getf plist :expand)) nil) ;; 第二个分支:存在:expand,开始筛选符合条件的键值对 (t (let ((result (list :expand (getf plist :expand)))) ;; 遍历属性列表,每次取一对键值 (loop for (key value) on plist by #'cddr ;; 把关键字转成字符串(注意关键字转字符串默认是大写的) for key-string = (string key) ;; 判断是否是:input*或:output*格式的键 when (or (string-starts-with-p key-string "INPUT") (string-starts-with-p key-string "OUTPUT")) do (setf result (nconc result (list key value)))) ;; 如果结果里除了:expand还有其他键值对,就返回,否则返回nil (if (cdr result) result nil)))))
代码解释:
- 前缀匹配函数:
string-starts-with-p用来判断字符串是否以指定前缀开头,很多现代Lisp实现(比如SBCL)已经内置了这个函数,如果你的环境有,就可以跳过自定义这一步。 - cond逻辑:第一个分支检查
:expand是否存在,不存在直接返回nil;第二个分支处理存在:expand的情况,完全符合你想用cond实现的需求。 - 遍历筛选:用
loop遍历plist(on plist by #'cddr是遍历键值对的标准写法),把每个关键字转成字符串后,检查是否以"INPUT"或"OUTPUT"开头(因为:input1转字符串是"INPUT1",所以要匹配大写前缀)。 - 结果组装:把
:expand的键值对放在最前面,然后把所有符合条件的:input*/:output*键值对追加进去,最后确保结果不为空(如果只有:expand没有其他符合条件的键,就返回nil)。
测试一下:
用你给出的示例plist:
(extract-expand-and-io '(:input1 1 :input2 2 :input3 3 :output1 10 :output2 20 :expand "string here"))
运行后会返回:
(:expand "string here" :input1 1 :input2 2 :input3 3 :output1 10 :output2 20)
如果你的plist里只有:expand没有:input*/:output*,比如(:expand "test" :other 5),函数会返回nil,完全符合你“存在:expand且存在:input或:output才执行操作”的要求。
如果需要更复杂的正则匹配(比如不是前缀,而是中间包含某个模式),可以用cl-ppcre库(Common Lisp的正则表达式库),但对于你当前的需求,前缀匹配已经足够简洁高效了。
内容的提问来源于stack exchange,提问作者MadPhysicist
相关产品推荐
相关产品推荐

