方案中的变量函数

时间:2012-10-08 10:17:06

标签: scheme variadic cdr

我必须在Scheme中定义一个可变参数函数,它采用以下形式: (define (n-loop procedure [a list of pairs (x,y)])其中对的列表可以是任意长度。

每对指定下限和上限。也就是说,以下函数调用:(n-loop (lambda (x y) (inspect (list x y))) (0 2) (0 3))产生:

(list x y) is (0 0)
(list x y) is (0 1)
(list x y) is (0 2)
(list x y) is (1 0)
(list x y) is (1 1)
(list x y) is (1 2)

显然,汽车和cdr将不得不参与我的解决方案。但是使这个困难的规定如下。根本没有使用赋值语句或迭代循环(while和for)。

我可以使用while来处理它,并且可以通过对列表索引,但看起来我必须使用递归。我不想要任何代码解决方案,除非你认为有必要进行解释,但有没有人建议如何对它进行攻击?

1 个答案:

答案 0 :(得分:4)

在Scheme中进行循环的标准方法是使用尾递归。事实上,让我们说你有这个循环:

(do ((a 0 b)
     (b 1 (+ a b))
     (i 0 (+ i 1)))
    ((>= i 10) a)
  (eprintf "(fib ~a) = ~a~%" i a))

实际上,宏观扩展为以下内容:

(let loop ((a 0)
           (b 1)
           (i 0))
  (cond ((>= i 10) a)
        (else (eprintf "(fib ~a) = ~a~%" i a)
              (loop b (+ a b) (+ i 1)))))

进一步将宏扩展到此(我不会对cond进行宏扩展,因为这与我的观点无关):

(letrec ((loop (lambda (a b i)
                 (cond ((>= i 10) a)
                       (else (eprintf "(fib ~a) = ~a~%" i a)
                             (loop b (+ a b) (+ i 1)))))))
  (loop 0 1 0))

你应该在这里看到letrec并思考,“啊哈!我看到了递归!”。确实你这样做(特别是在这种情况下,尾递归,虽然letrec也可以用于非尾递归)。

Scheme中的任何迭代循环都可以重写为(命名let版本是如何在Scheme中以惯用方式编写循环,但是如果你的赋值不允许你使用named { {1}},进一步扩展并使用let)。我上面描述的宏扩展是直截了当的,机械的,你应该能够看到如何将其转换为另一个。


由于你的问题询问了可变函数的含义,你可以用这种方式编写一个可变函数:

letrec

(这是BTW,一种编写(define (sum x . xs) (if (null? xs) x (apply sum (+ x (car xs)) (cdr xs)))) 函数的非常低效的方式;我只是用它来演示如何发送(使用sum)和接收(使用不正确的lambda列表) )任意数量的论点。)


更新

好的,所以这里有一些一般的建议:你需要两个循环:

  1. 一个外部循环,它通过范围级别(这是你的可变参数)
  2. 一个内循环,遍历每个范围级别的数字
  3. 在每个循环中,仔细考虑:

    1. 起始条件是什么
    2. 结束条件是什么
    3. 您希望在每次迭代中做什么
    4. 是否需要在迭代之间保留任何状态
    5. 特别要仔细考虑最后一点,因为在给定任意数量的嵌套级别的情况下,您将如何嵌套循环。 (在我的下面的示例解决方案中,这就是apply变量。)

      在确定了所有这些内容之后,您可以构建解决方案的一般结构。我将在下面发布我的解决方案的基本结构,但在你查看我的代码之前,你应该好好考虑一下如何解决问题,因为它会让你很好地理解它们之间的差异。您的解决方案和我的解决方案,它将帮助您更好地理解我的代码。

      另外,不要害怕先使用命令式循环(如cur)来编写它,然后在它全部工作时将其转换为等效的do。只需重读第一部分,了解如何进行转换。

      所有这些都说,这是我的解决方案(具体细节被删除):

      let

      请记住,一旦您完成此工作,您仍然需要将(define (n-loop proc . ranges) (let outer ((cur ???) (ranges ranges)) (cond ((null? ranges) ???) (else (do ((i (caar ranges) (+ i 1))) ((>= i (cadar ranges))) (outer ??? ???)))))) 循环转换为基于命名do的循环。 (或者,您可能需要更进一步,将外部和内部循环转换为let形式。)