代码之家  ›  专栏  ›  技术社区  ›  Willi Ballenthin

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

  •  3
  • Willi Ballenthin  · 技术社区  · 16 年前

    最近我写了更多的Lisp代码。特别是递归函数,它获取一些数据,并构建结果数据结构。有时,除了用户提供的数据之外,我似乎还需要向函数的下一次调用传递两三条信息。我们称之为累加器。

    组织这些代码接口的最佳方法是什么?

    现在,我做了这样的事情:

    (defun foo (user1 user2 &optional acc1 acc2 acc3)
        ;; do something
        (foo user1 user2 (cons x acc1) (cons y acc2) (cons z acc3)))
    

    我很想这样做,但我很担心,因为我不需要展示&编程器的可选参数。

    我正在考虑的三种方法:

    • 有一个包装器函数,鼓励用户使用它来立即调用扩展定义。

    • labels 在签名简洁的函数内部。

    谢谢你们!

    3 回复  |  直到 16 年前
        1
  •  4
  •   Nathan Shively-Sanders    16 年前

    如果您想编写习惯用法的commonlisp,我建议您使用循环和变量进行迭代。递归很酷,但它只是普通Lisper的众多工具之一。此外,通用Lisp规范不能保证尾部调用消除。

    也就是说,我推荐 labels 方法如果你有一个结构,例如一棵树,它不可避免地是递归的,你无论如何都不能得到尾部调用。可选参数让您的实现细节泄露给调用者。

        2
  •  2
  •   Jonathan Feinberg    16 年前

    我认为,你对用户屏蔽实现细节的冲动是明智的。我不知道CommonLisp,但在Scheme中,您可以通过在公共函数的词法作用域中定义helper函数来实现。

    (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中可见的名称,然后调用该函数。

        3
  •  0
  •   Svante    16 年前

    我已经使用了你提到的所有选项。都有各自的优点,所以归根结底是个人喜好。

    我已经开始使用我认为合适的东西了。如果我认为 &optional API中的累加器可能对用户有意义,我将其保留在中。例如,在 reduce -与函数类似,用户可以使用累加器来提供起始值。否则,我会经常把它改写成 loop , do iter (来自迭代器库)形式,如果将其视为有意义的话。有时候 labels 也可以使用helper。