第一个问题:假设Common Lisp的现代编译器通常将(mapcar #'fn ...)
和(map 'list #'fn ...)
编译成与(mapc #'fn ...)
相同的代码是否合理?也就是说,假设编译器会看到忽略返回值是否合理,以便不需要构造新的列表?例如假设我的源文件包含以下代码:
(defun set-foo-5 (sym)
(setf (get sym 'foo) 5))
(progn
(mapcar #'set-foo-5 '(a b c))
(format t "All foos are five!~%"))
mapc
会更有效吗?我经常运行SBCL,但我的猜测是任何好的编译器都能够发现在这种情况下不需要占用新的列表。我是对的吗?
第二个问题:在相同的情况下,我应该假设现代编译器通常会将map 'list
编译为与mapcar
相同的代码,只要源代码中有'list
即可代码,而不是在运行时选择?
第三个问题:其他序列的类似问题。例如,如果我用mapcar
替换上面预测中的(map 'vector #'set-foo-5 #(a b c))
行,我应该假设编译后的代码不会费心构造新的向量吗?
答案 0 :(得分:10)
首先,map
和mapcar
在一个非常重要的方面有所不同:后者在lists上运行,而前者在任何{{3}上运作}}。这意味着只有当编译器证明所有数据时,(map nil ...)
(或(map 'list ...)
)才等同于(mapc ...)
(resp。(mapcar ...)
)参数是list
s。
第二,大多数现代Lisp编译器(例如sequences)通常都足以弄清楚这些事情(必要时使用声明)。
第三次,唯一可以确定的方法是使用SBCL。
第四,函数选择是一种记录代码的方式。当您使用map
时,您告诉人类读者您的代码将要将非列表传递给该函数。如果您确定只使用列表,为什么要混淆读者(几个月后你自己)?
第五,disassemble
。
答案 1 :(得分:3)
使用@ sds的答案中的一些提示,我意识到有一些简单的初步测试可以回答我对特定实现的问题(无需通过dissemble
输出)。当mapcar
的返回值被忽略时,SBCL和CCL似乎不一定以与mapc
相同的方式对待mapcar
。首先将lis1
和lis2
定义为显着长度的列表(我的长度为100,000,并包含整数),然后多次运行mapcar
,mapc
等,如下所示(可选择初步调用gc
清除旧垃圾):
(gc :full t)
(time
(progn
(dotimes (ignored 1000)
(mapcar #'+ lis1 lis2))
(format t "mapcar:~%")))
(gc :full t)
(time
(progn
(dotimes (ignored 1000)
(mapc #'+ lis1 lis2))
(format t "mapc:~%")))
(gc :full t)
(time
(progn
(dotimes (ignored 1000)
(map nil #'+ (the list lis1) (the list lis2)))
(format t "map nil with lists~%")))
例如,我机器上的SBCL产生:
mapcar:
Evaluation took:
2.306 seconds of real time
2.287627 seconds of total run time (2.136130 user, 0.151497 system)
[ Run times consist of 0.147 seconds GC time, and 2.141 seconds non-GC time. ]
99.22% CPU
3,683,188,504 processor cycles
1,600,049,536 bytes consed
mapc:
Evaluation took:
0.639 seconds of real time
0.638733 seconds of total run time (0.638011 user, 0.000722 system)
100.00% CPU
1,020,310,296 processor cycles
0 bytes consed
map nil with lists
Evaluation took:
0.592 seconds of real time
0.592114 seconds of total run time (0.591199 user, 0.000915 system)
100.00% CPU
945,957,944 processor cycles
0 bytes consed
这些是默认优化设置的典型结果。使用declaim
优化速度,非安全性等可以加快速度,但不会改变mapc
和map nil
比mapcar
和mapcar
快一个数量级的事实。 {1}} {{1}}做了很多事情。 CCL的结果相似,但总体上较慢。