我是 CHICKEN 和 Scheme 的新手。在我寻求理解尾递归的过程中,我写道:
(define (recsum x) (recsum-tail x 0))
(define (recsum-tail x accum)
(if (= x 0)
accum
(recsum-tail (- x 1) (+ x accum))))
这符合我的预期。但是,这似乎有点重复;有一个可选参数应该使它更整洁。所以我尝试了:
(define (recsum x . y)
(let ((accum (car y)))
(if (= x 0)
accum
(recsum (- x 1) (+ x accum)))))
但是,在 CHICKEN(也许在其他方案实现中(,car
不能用于()
:
Error: (car) bad argument type: ()
有没有另一种方法来实现可选的函数参数,特别是在 CHICKEN 5 中?
我认为您正在寻找一个命名的let
,而不是可选的过程参数。这是一种使用(可能(额外参数定义帮助程序过程的简单方法,您可以根据需要初始化这些参数:
(define (recsum x)
(let recsum-tail ((x x) (accum 0))
(if (= x 0)
accum
(recsum-tail (- x 1) (+ x accum)))))
当然,我们也可以使用 varargs 实现它 - 但我认为这看起来并不优雅:
(define (recsum x . y)
(let ((accum (if (null? y) 0 (car y))))
(if (= x 0)
accum
(recsum (- x 1) (+ x accum)))))
无论哪种方式,它都可以按预期工作:
(recsum 10)
=> 55
Chicken 有可选的参数。你可以这样做:
(define (sum n #!optional (acc 0))
(if (= n 0)
acc
(sum (- n 1) (+ acc n))))
但是,我将投票反对使用它,因为它是非标准方案。鸡说他们支持 SRFI-89:可选的位置和命名参数,但它似乎是早期版本,鸡蛋需要重做。无论如何,当它被重新应用时,这应该可以工作:
;;chicken-install srfi-89 # install the egg
(use srfi-89) ; imports the egg
(define (sum n (acc 0))
(if (= n 0)
acc
(sum (- n 1) (+ acc n))))
你使用休息参数的想法也有效。但请记住,该过程随后将在每次迭代的堆上构建一个pair
:
(define (sum n . acc-lst)
(define acc
(if (null? acc-lst)
0
(car acc-lst)))
(if (= n 0)
acc
(sum (- n 1) (+ acc n))))
所有这些都泄露了内部信息。有时,具有可选参数是公共合约的一部分,但在这种情况下,这是为了避免多写几行。通常你不希望有人传递第二个参数,你应该保持内部的私密性。更好的方法是使用命名let
并保持公共合同不变。
(define (sum n)
(let loop ((n n) (acc 0))
(if (= n 0)
acc
(loop (- n 1) (+ acc n))))