如何定义带参数的MULTIPLE-OF类型说明符及同类多参数类型?
Common Lisp动态参数类型说明符的合规实现方案
先明确问题:Paul Graham《ANSI Common Lisp》第234页的multiple-of示例违反了ANSI规范——CLHS规定SATISFIES子句只能接受符号,不能直接使用lambda表达式,执行(typep 12 '(multiple-of 4))会触发类型错误。下面给出两种场景的合规实现方式:
一、正确实现(multiple-of n)类型说明符
核心思路是把原本的lambda闭包绑定到一个唯一命名的函数符号上,让SATISFIES可以合法引用。这里提供两种实现方式:
1. 基础动态生成版
每次定义类型时生成唯一的函数符号,绑定对应的检查逻辑:
(deftype multiple-of (n) (let ((predicate-sym (gensym (format nil "MULTIPLE-OF-~A-P-" n)))) (setf (fdefinition predicate-sym) (lambda (x) (zerop (mod x n)))) `(and integer (satisfies ,predicate-sym))))
这种方式简单直接,但相同n值会重复生成不同的函数符号,适合临时使用。
2. 缓存优化版
用哈希表缓存相同参数对应的谓词函数,避免重复生成:
(defvar *multiple-of-pred-cache* (make-hash-table)) (deftype multiple-of (n) (let ((predicate-sym (or (gethash n *multiple-of-pred-cache*) (let ((new-sym (gensym (format nil "MULTIPLE-OF-~A-P-" n)))) (setf (fdefinition new-sym) (lambda (x) (zerop (mod x n))) (gethash n *multiple-of-pred-cache*) new-sym) new-sym)))) `(and integer (satisfies ,predicate-sym))))
多次调用(multiple-of 4)会复用同一个函数符号,更高效。
二、实现多参数类型说明符(类似(integer * *))
以实现(between low high)类型(检查数值在[low, high]区间内)为例,同样采用缓存+动态函数的思路:
(defvar *between-pred-cache* (make-hash-table :test #'equal)) (deftype between (low high) (let ((cache-key (cons low high)) predicate-sym) (setf predicate-sym (or (gethash cache-key *between-pred-cache*) (let ((new-sym (gensym (format nil "BETWEEN-~A-~A-P-" low high)))) (setf (fdefinition new-sym) (lambda (x) (and (>= x low) (<= x high))) (gethash cache-key *between-pred-cache*) new-sym) new-sym))) `(and number (satisfies ,predicate-sym))))
调用示例:
(typep 7 '(between 3 10)) ; 返回T (typep 15 '(between 3 10)) ; 返回NIL
内容的提问来源于stack exchange,提问作者Jérôme Radix
相关产品推荐
相关产品推荐

