尾递归函数的用户友好包装

User-friendly wrappings for tail recursive functions

我正在努力学习普通的 lisp。我熟悉尾递归,但我不熟悉以调用者不必初始化累加器变量的方式包装尾递归函数的惯用方法。这是一个例子:

(defun add-em (n s) 
    (if (eql n 0) 
        s 
        (add-em (- n 1) (+ s n))
    )
) 

说我想包装这个函数,这样用户只需要管理输入 n 而不需要完整的函数调用 (add-em <number> 0)。在其他语言中,例如 scala,我会定义一个内部函数,然后在外部函数的末尾我会调用尾递归内部函数来 运行 算法。

在普通的 lisp 中,我可以在函数中定义一个 lambda 并使用它,但它看起来有点难看。我认为可能有更惯用的方法来做到这一点,但谷歌搜索并没有真正给我任何结果。

除了完全拆分功能之外,还有更惯用的方法吗?或者这是最好的方法?一个例子:

(defun add-em-inner (num sum)
       (if (eql num 0)
       sum
       (add-em-inner (- num 1) (+ num sum))
       )
)

(defun add-em (n) 
  (add-em-inner n 0)
)

一种方法是使用 labels 运算符来定义一个递归的词法函数。也就是说:

(defun add-em-inner (num sum)
       (if (eql num 0)
           sum
           (add-em-inner (- num 1) (+ num sum))))

(defun add-em (n) 
  (add-em-inner n 0))

变成这样:

(defun add-em (n) 
  (labels ((add-em-inner (num sum)
             (if (eql num 0)
               sum
               (add-em-inner (- num 1) (+ num sum)))))
    (add-em-inner n 0)))

如果您不介意额外的累加器是函数 public 接口的一部分,但只关心用户的便利性(调用者不必指定值),您可以将其设为可选参数:

(defun add-em (n &optional (s 0)) 
    (if (eql n 0) 
        s 
        (add-em (- n 1) (+ s n)))) 

通常有充分的理由不这样做;例如,您可能希望保留为向后兼容的未来 API 扩展定义可选参数的能力。这在这里仍然是可能的,但前提是外部调用者不传递该参数。