在函数中包含累加器等参数的最佳实践是什么?

时间:2010-02-18 15:12:35

标签: recursion coding-style lisp

我最近一直在编写更多的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

  • 刚开始使用循环和变量。但是,我不喜欢,因为我想真正地围绕递归。

谢谢你们!

3 个答案:

答案 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的函数中,用户可以使用累加器提供起始价值。否则,我会经常将其重写为loopdoiter(来自迭代库)表单,如果有意义将其视为这样。有时,也会使用labels助手。