简体   繁体   English

在Scheme中的相互递归中获得尾调用优化

[英]Getting tail call optimization in mutual recursion in Scheme

While developing a classical exercise piece of code for odd and even functions in MIT/GNU Scheme (rel 9.2 ), I encountered a problem that my code does not terminate for a big integer value.MIT/GNU Scheme (rel 9.2 ) 中为oddeven函数开发一段经典的代码练习时,我遇到了一个问题,我的代码不会因大整数值而终止。 First, I tested the following code, which processes both positive and negative values:首先,我测试了以下代码,它处理正值和负值:

(define error-message-number "Error. x must be a number")

(define odd?
  (lambda (x)
    (cond 
      ((not (integer? x)) error-message-number)
      ((= x 0) #f)
      ((< x 0) (even? (+ x 1))) ;for negatives
      (else (even? (- x 1)))))) ;if number n is odd then n - 1 is even 

(define even?
  (lambda (x)
    (cond 
      ((not (integer? x)) error-message-number)
      ((= x 0) #t)
      ((< x 0) (odd? (+ x 1))) ;for negatives
      (else (odd? (- x 1)))))) ;if number n is even then n - 1 is odd

The calls (assert (equal? #f (even? 100000000001))) and (assert (equal? #t (odd? -100000000001))) do not terminate on my machine, while, eg (assert (equal? #f (even? 1001))) and (assert (equal? #t (odd? -1001))) do.调用(assert (equal? #f (even? 100000000001)))(assert (equal? #t (odd? -100000000001)))不会在我的机器上终止,而例如(assert (equal? #f (even? 1001)))(assert (equal? #t (odd? -1001)))做。 My first thought was that the code is not optimized for proper tail recursion, while I have not one, but two tail calls in each function.我的第一个想法是代码没有针对正确的尾递归进行优化,而我没有一个,而是每个函数中的两个尾调用。 So, I decided to simplify the task and make a take for positive integers only, like this:因此,我决定简化任务并仅对正整数进行处理,如下所示:

(define error-message-positive "Error. x must be a nonnegative number")

(define odd-positive?
  (lambda (x)
    (cond 
      ((not (integer? x)) error-message-number)
      ((= x 0) #f)
      ((< x 0) error-message-positive) ;for negatives
      (else (even? (- x 1)))))) ;if number n is odd then n - 1 is even 

(define even-positive?
  (lambda (x)
    (cond 
      ((not (integer? x)) error-message-number)
      ((= x 0) #t)
      ((< x 0) error-message-positive) ;for negatives
      (else (odd? (- x 1)))))) ;if number n is even then n - 1 is odd

But this version does not return either for big integers.但是这个版本不会返回大整数。 So, I have these related questions :所以,我有这些相关的问题

  1. Features.特征。 Are mutually recursive functions in MIT/GNU Scheme optimized at all? MIT/GNU Scheme 中的相互递归函数是否经过优化?
  2. Diagnostics.诊断。 Is there any way one can tell the functions were indeed optimized for mutual tail recursion by Scheme compiler/interpreter.有什么办法可以告诉我们,Scheme 编译器/解释器确实针对相互尾递归优化了这些函数。 So, how can one tell the problem is in (absence of) tail recursion optimization, or in some other thing.那么,如何判断问题出在(没有)尾递归优化中,还是出在其他方面。
  3. What is proper mutual tail recursion?什么是正确的相互尾递归? Does my initial code qualify for optimization?我的初始代码是否符合优化条件? Does my second take qualify for it?我的第二次拍摄是否符合条件?
  1. What is proper mutual tail recursion?什么是正确的相互尾递归?

Just like your code, both versions of them.就像你的代码一样,它们的两个版本。

  1. Diagnostics.诊断。

Empirical orders of growth FTW! FTW 的经验增长顺序

Your diagnosis just might be incorrect.您的诊断可能不正确。 In particular, on my machine, in Racket, the expected time of your code to finish is 40 minutes .特别是,在我的机器上,在 Racket 中,您的代码完成的预期时间是40分钟 And it does seem to run in constant memory overall.它似乎确实在整体内存中运行。

Yes, even while running in constant space it still takes time linear in the magnitude of argument.是的,即使在恒定空间中运行,它仍然需要与参数大小成线性关系的时间。 How do I know?我怎么知道? I simply measured it in clock wall time, and it indeed scales as linear ie n 1 power law.我只是用时钟墙上的时间来测量它,它确实是线性的,即n 1幂律。 Approximately.大约。 (ie whether it measured as 1.02 or 0.97, it still indicates linear growth rate. approximately. which is all that matters). (即,无论测量为 1.02 还是 0.97,它仍然表示线性增长率。大约。这才是最重要的)。

See also:也可以看看:

  1. Features.特征。 Are mutually recursive functions in MIT/GNU Scheme optimized at all? MIT/GNU Scheme 中的相互递归函数是否经过优化?

It must be so, since tail call optimization is in the language specification.必须如此,因为尾调用优化在语言规范中。 And TCO is more than just tail recursion, as I understand it even if the decision what to call next is dynamic (let alone static, evident in code as it is in your case) it still must run in constant stack space when one tail call eventually leads back to entering the same function again.并且 TCO 不仅仅是尾递归,据我所知,即使决定接下来调用什么是动态的(更不用说是静态的,在您的情况下在代码中很明显)它仍然必须在一个尾调用时在恒定的堆栈空间中运行最终导致再次进入相同的功能。 Tail call is tail call, whatever is called, and it must be optimized.尾调用就是尾调用,不管叫什么,都要优化。 I don't have an official quotation ready at the moment.我目前没有准备好正式报价单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM