嵌套列表中rassoc返回NIL而find正常,是误用还是Bug?
rassoc函数 核心问题出在rassoc的设计语义和你的数据结构不匹配,下面具体分析:
你的数据结构
先看mlist的结构:
(setq mlist (list (list (cons "A" 0) (make-hash-table)) (list (cons "B" 1) (make-hash-table))))
每个顶层元素是列表,而非rassoc预期的cons对(关联列表alist的元素必须是cons对)。每个顶层列表的第一个元素才是cons对,第二个是哈希表。
各函数行为分析
1. assoc调用
(format t "~A~%" (assoc "A" mlist :key #'car :test #'equal))
你指定:key #'car后,assoc会遍历mlist的每个顶层元素x,取(car x)(即cons对("A" . 0)),再用equal比较"A"和这个cons对。实际上(equal "A" ("A" . 0))的结果是nil,理论上应该返回nil——你觉得它“按预期工作”可能是对返回值的误解,或者实际代码存在差异。
2. find调用
(format t "~A~%" (find 0 mlist :key #'cdar :test #'equal))
cdar等价于(cdr (car x)),即取顶层元素x的第一个元素(cons对)的cdr值(0或1),和item 0比较后匹配成功,所以能找到对应的顶层列表,这是正确的用法。
3. rassoc调用
(format t "~A~%" (rassoc 0 mlist :key #'car :test #'equal))
rassoc是专门为元素为cons对的关联列表设计的:它会对每个元素(你这里是顶层列表x)应用:key函数得到(car x)(即cons对("A" . 0)),然后比较item 0和这个值是否满足equal——显然0和("A" . 0)不相等,所以返回nil,这是符合语义的正常结果。
正确的用法
如果你想通过值(0、1)找到对应的cons对,应该先提取所有cons对组成标准的关联列表,再调用rassoc:
(rassoc 0 (mapcar #'car mlist) :test #'equal)
这样会返回("A" . 0),符合预期。
如果你想直接在mlist中找到对应的顶层列表,继续用find并指定:key #'cdar即可,因为mlist并非rassoc设计的关联列表结构,不适合直接用rassoc处理。
关于HyperSpec的说明
文档中(rassoc item list :test fn)等价于(find item list :test fn :key #'cdr)的描述,前提是list是标准关联列表(元素为cons对),此时#'cdr是取每个cons对的cdr值。你的mlist不符合这个结构,所以不能直接套用该等价关系。
内容的提问来源于stack exchange,提问作者myselfesteem

