我最近一直在编写更多的Lisp代码。特别是,递归函数,它接收一些数据,并构建结果数据结构。除了用户提供的数据之外,有时似乎我需要将两到三条信息传递给函数的下一次调用。让我们称这些累加器。
将这些接口组织到我的代码的最佳方法是什么?
目前,我这样做:
(defun foo (user1 user2 &optional acc1 acc2 acc3)
;; do something
(foo user1 user2 (cons x acc1) (cons y acc2) (cons z acc3)))
这是我喜欢的,但我很担心,因为我不需要向程序员提供&可选参数。
我正在考虑的3种方法:
有一个包装函数,鼓励用户立即调用扩展的定义。
在签名简洁的函数内部使用labels
。
刚开始使用循环和变量。但是,我不喜欢,因为我想真正地围绕递归。
谢谢你们!
答案 0 :(得分:4)
如果你想编写惯用的Common Lisp,我建议使用循环和变量进行迭代。递归很酷,但它只是Common Lisper的许多工具。此外,Common Lisp规范不保证尾部调用消除。
那就是说,如果你有一个结构,例如一棵树,我推荐使用labels
方法,这是不可避免的递归,你无论如何都不能得到尾调用。可选参数允许您的实现细节泄露给调用者。
答案 1 :(得分:2)
我认为,您向用户屏蔽实施细节的冲动很明智。我不知道常见的lisp,但是在Scheme中你可以通过在公共函数的词法范围中定义你的辅助函数来实现它。
(define (fibonacci n)
(let fib-accum ((a 0)
(b 1)
(n n))
(if (< n 1)
a
(fib-accum b (+ a b) (- n 1)))))
let
表达式定义了一个函数,并将其绑定到只在let中可见的名称,然后调用该函数。
答案 2 :(得分:0)
我已经使用了你提到的所有选项。所有这些都有其优点,因此归结为个人偏好。
我已经到了使用我认为合适的任何东西。如果我认为将&optional
累加器留在API中可能对用户有意义,我将其保留。例如,在类似reduce
的函数中,用户可以使用累加器提供起始价值。否则,我会经常将其重写为loop
,do
或iter
(来自迭代库)表单,如果有意义将其视为这样。有时,也会使用labels
助手。