为什么在Racket代码中循环这么慢

时间:2017-01-03 13:01:37

标签: loops for-loop scheme racket

我试图找到最小的非除数数(https://codegolf.stackexchange.com/questions/105412/find-the-smallest-number-that-doesnt-divide-n)。以下版本使用'命名为let'工作正常:

(define (f1 m)
  (let loop ((n 2))
    (cond
      [(= 0 (modulo m n))
       (loop (+ 1 n))]
      [else n])))

我正在测试:

(f 24)
(f 1234567)
(f 12252240)
(f 232792560)

以上版本产生以下输出:5 2 19和23。

但是,使用内置for循环的版本非常慢并且实际上因内存不足错误导致崩溃并且数字较大:

(define (f2 m)
  (for/first ((i (range 2 m))
              #:when (not (= 0 (modulo m i)))
              #:final (not (= 0 (modulo m i))))
    i))

第二版的代码是否存在错误,或者与Racket中的命名let相比,for循环效率低吗?

2 个答案:

答案 0 :(得分:6)

range函数实际上分配了一个列表,因此函数的时间由巨大的列表分配支配。请改用in-range

(define (f2 m)
  (for/first ([i (in-range 2 m)]
              #:when (not (= 0 (modulo m i)))
              #:final (not (= 0 (modulo m i))))
    i))

有关不同序列形式相对速度的更多说明,另请参阅“球拍指南”中的for performance部分。

答案 1 :(得分:0)

在我看来,递归通常更适合于Racket而不是循环。我会这样解决问题...

(define start-counter 1)

(define (f number counter)
   (cond [(not (= (modulo number counter) 0)) counter]
         [else (f number (add1 counter))]))

(define (f2 number)
   (f number start-counter))