英文:
Recursion vs apply in Common Lisp?
问题
当我阅读代码以学习目的时,我经常看到这样的函数:
(defun min-list (lst)
(cond ((endp (rest lst)) (first lst))
(t
(min (first lst) (min-list (rest lst))))))
如果我在我的项目中编写这个函数,我会使用 apply
来写:
(defun min-list2 (lst)
(cond ((endp (rest lst)) (first lst))
(t
(apply #'min lst)))))
当我使用 time
测试这两种变体时,它们似乎执行相同。
这有关系吗?
英文:
When I am reading code for learning purposes, I often see functions like this:
(defun min-list (lst)
(cond ((endp (rest lst)) (first lst))
(t
(min (first lst) (min-list (rest lst))))))
If I were writing this function in my project, I would have written it with apply
.
(defun min-list2 (lst)
(cond ((endp (rest lst)) (first lst))
(t
(apply #'min lst))))))
When I test these two variations with time
, they seem to perform the same.
Does it matter?
答案1
得分: 7
他们使用不同的风格,有不同的限制。
递归变体,如所示,受堆栈空间的限制(如果列表太长,将在某个时候导致堆栈溢出)。可以重写它以使其成为尾递归,大多数CL实现将优化为几乎不增长堆栈的循环,但对于可移植代码来说并不保证。
apply
变体受 call-arguments-limit
限制,根据标准,它至少保证为50,尽管大多数实现都要高得多。通常最好明确使用 reduce
代替。
另一种也是惯用的解决方案是直接使用 loop
、do
或某些库,如 iterate
或 for
进行循环,这些通常是性能最好的(在需要性能的情况下)。
英文:
They use different styles with different limitations.
The recursion variant, as it is written there, is limited by the space for the stack (you get a stack overflow at some point if the list is too long). It is possible to rewrite it to be tail recursive, which in most CL implementations will be optimized to more-or-less a loop that doesn't grow the stack, but that's not guaranteed for portable code.
The apply
variant is limited by call-arguments-limit
, which is only guaranteed to be at least 50 by the standard, although most implementations have it much higher. It is usually better to explicitly use reduce
instead.
Another, also idiomatic, solution is direct looping using loop
, do
, or some library such as iterate
or for
, and these are usually the most performant (in situations where that matters).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论