Common Lisp如何用check-type校验键值均为整数的哈希表
问题解答
完全可以通过check-type实现你要的全表键值类型校验,相比依赖内层循环隐式抛错,入口显式校验是更稳妥的工程实践。
自定义类型实现check-type校验
check-type接受任意合法的Common Lisp类型说明符,你可以通过satisfies类型搭配自定义谓词,定义「所有键、值均为整数的哈希表」类型,步骤如下:
- 先编写判断哈希表键值类型的谓词:
(defun integer-kv-hash-table-p (obj) (and (hash-table-p obj) (loop for key being each hash-key of obj using (hash-value val) always (and (integerp key) (integerp val)))))
- 用
deftype为该类型定义别名,方便后续复用:
(deftype integer-kv-hash-table () "Hash table whose all keys and values are integers." '(satisfies integer-kv-hash-table-p))
- 替换原函数中的
check-type声明即可,还可以自定义错误提示信息:
(defun distribution-to-list (distribution) (check-type distribution integer-kv-hash-table "a hash table with integer keys and non-negative integer values") (loop for key being each hash-key of distribution using (hash-value value) nconc (loop repeat value collect key)))
补充说明:如果你的业务要求值必须是可作为repeat参数的非负整数,可以把谓词里的(integerp val)改成(typep val '(integer 0 *)),校验会更精准。
关于两种错误处理方式的选择
直接依赖内层loop的repeat子句抛错虽然能触发类型错误,但并不属于良好实践,核心问题有两个:
- 错误定位模糊:
repeat子句抛出的错误只会提示计数参数类型非法,不会明确告知错误根源是传入的哈希表不符合结构要求,调用方排查问题需要顺着调用栈逐层定位,成本很高。 - 不符合故障快速(fail-fast)原则:如果非法值出现在哈希表遍历的中后段,函数已经执行了部分列表拼接操作才抛错,会留下不完整的中间状态,对有重试、状态回滚逻辑的场景非常不友好;入口校验会在函数执行任何业务逻辑前就拦截非法输入,不会产生副作用。
如果面对的是体量极大的哈希表,你可以根据性能需求权衡是否要做全表预校验——但考虑到该函数本身就需要遍历全表生成列表,预校验的O(n)开销和主逻辑同量级,绝大多数场景下这点成本完全可以接受。
内容的提问来源于stack exchange,提问作者brubsby
相关产品推荐
相关产品推荐

