我必须在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来处理它,并且可以通过对列表索引,但看起来我必须使用递归。我不想要任何代码解决方案,除非你认为有必要进行解释,但有没有人建议如何对它进行攻击?
答案 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列表) )任意数量的论点。)
好的,所以这里有一些一般的建议:你需要两个循环:
在每个循环中,仔细考虑:
特别要仔细考虑最后一点,因为在给定任意数量的嵌套级别的情况下,您将如何嵌套循环。 (在我的下面的示例解决方案中,这就是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
形式。)