Common Lisp中mapcan是否真的使用nconc的技术疑问
为什么
mapcan能成功,而(apply #'nconc (mapcar ...))会失败? 首先得澄清:CLHS里对mapcan的功能描述是将mapcar的结果用nconc拼接,但它的实际实现绝不会用apply来传递所有子列表——这就是两者结果不同的核心原因。
参数数量限制的影响
apply的参数总数(包括被调用的函数本身)不能超过call-arguments-limit:
- 当你执行
(apply #'nconc (mapcar #'list ...))时,mapcar会生成和输入列表长度相同的子列表,这些子列表都会作为单独参数传给nconc。比如输入列表长度等于call-arguments-limit(LispWorks里是2047),那总参数数就是2047+1=2048,直接超过限制,触发错误。 - 而
mapcan的实现逻辑是逐个遍历mapcar的结果,每次只把当前累积的结果和下一个子列表传给nconc,类似这样:
这种方式每次调用(let ((output nil)) (dolist (sublist (mapcar #'list input-list)) (setf output (nconc output sublist))) output)nconc只传2个参数,完全不会触及call-arguments-limit的限制。
关于CLHS的等价式
你看到的mapcon等价于(apply #'nconc (maplist ...))是语义等价,不是强制实现要求。CLHS的这类等价式只是用来描述功能,不是说实现必须这么写——如果真用apply实现,在参数数量接近限制时必然出错,不符合语言的健壮性要求。所以实际的mapcan和mapcon都会用循环逐个拼接,而非apply。
内容的提问来源于stack exchange,提问作者massysett
相关产品推荐
相关产品推荐

