用点尾符号表示的方案递归

问题描述

我正在尝试进行SICP的练习2.20。它引入了点尾符号。在完成练习之前,我需要帮助您了解我编写的此测试程序有什么问题:

(define (f . b)
     (if (null? b) '() (cons (car b) (f . (cdr b)))))

当我在解释器中输入(f 1 2 3)时,没有得到我期望的(1 2 3),而是出现了“ 最大递归深度超过”错误。

我看不到我做错了什么。 b是列表(1 2 3),所以我应该得到(cons 1 (f . (2 3)) => (cons 1 (cons 2 (f . (3)))) => (cons 1 (cons 2 (cons 3 (f . ())))) => (1 2 3)

我怀疑问题在于,点分符号仅适用于define,但是我想编写一个递归函数。我该怎么做?

解决方法

点尾符号可以与define一起使用,这意味着多余的参数在列表中一起收集。使用OP (define (f . b) ;...),在像(f 1 2 3)这样的函数调用中,b绑定到函数主体中的列表(1 2 3)。但是,您不能在函数调用中使用点尾符号。

但是仅从尝试的函数调用(f . (cdr b))中删除点也是行不通的。问题是(cdr b)是一个列表,因此在第一次递归调用之后,您有了(f (cdr b)),这意味着传递给f的值是列表(cdr b),并且由于该函数的定义方式,该列表被收集到另一个列表中并绑定到新函数主体中的b

如果初始调用为(f 1 2 3),则参数将被收集到一个列表中,以便在第一次调用中将b绑定到(1 2 3)。这样(car b)将是1,随后的调用将是(f (2 3))。然后,将参数(2 3)收集到一个列表中,以便在第二个调用中将b绑定到((2 3))。现在(car b)(2 3),这根本不是想要的。

本书apply中的稍后部分将被介绍,这为解决难题提供了一条途径:

(define (g . b)
  (if (null? b)
      '()
      (cons (car b) (apply g (cdr b)))))

但是,由于我们还不了解apply,因此我们必须具有创造力。另一种方法是在f内定义一个辅助函数,该函数接受一个列表参数而不是任意数量的参数:

(define (f . b)
  (define (helper xs)
    (if (null? xs)
        '()
        (cons (car xs) (helper (cdr xs)))))
  (helper b))

现在,递归调用到helper,它期望一个列表参数。

示例互动:

> (g 1 2 3)
(1 2 3)
> (f 1 2 3)
(1 2 3)

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...