Scheme / Lisp中的迭代范围函数

时间:2016-02-14 10:18:09

标签: scheme range lisp racket

我想实现范围函数(start,end,inc),它从头到尾生成所有自然数,并增加inc。 这是第一个版本:

(define (gen start end step)
  (if (>= start end)
  '()
  (cons start (gen (+ step start) end step))))

问题是,这不是尾递归。另一个尝试:

(define (gen-iter start end step acc)
  (if (>= start end)
    acc
    (gen-iter (+ step start) end step (cons start acc))))

但这会以相反的顺序生成一个列表:) 所以,是的,我可以用O(n)来反转它,并且很高兴,但我有点卡在这里,试图创建一个函数,从头到尾构建一个具有正确顺序的列表,不附加每次迭代< / strong>,因为附加费用很高。 还有一个内置列表,但我不知道它是如何运作的。

2 个答案:

答案 0 :(得分:4)

正如uselpa在评论中已经说过,解决方案是在最后使用dynamic_cast<>。扩展后,static_cast<>的球拍实施与reverse range功能的结果非常相似,除了您可能没有想到的某些情况。

球拍中reverse的实际实施使用gen-iter,但扩展到的实际上等同于

range

除了使用(for/list ([i (in-range start end step)]) i)函数而不是(reverse (for/fold ([fold-var null]) ([i (in-range start end step)]) (cons i fold-var))) 之外,它使用alt-reverse来更好地报告错误。如果你扩展它,它在经过一些简化(删除不必要的reverse包装,错误检查等)之后就相当于这个。)

for/fold/derived

命名为let,相当于定义一个这样的辅助函数:(在替换一些let-values并将助手提升到(reverse (let ([start start] [end end] [inc step]) (let for-loop ([fold-var null] [pos start]) (if (if (>= step 0) (< pos end) (> pos end)) (let ([i pos]) (let ([fold-var (cons i fold-var)]) (for-loop fold-var (+ pos inc)))) fold-var)))) 函数之外)之后

let

这仍然与您的解决方案不同,因为您的解决方案假定range为正,而即使(define (range start end step) (reverse (range-reversed null start end step))) (define (range-reversed fold-var pos end step) (if (if (>= step 0) (< pos end) (> pos end)) (range-reversed (cons pos fold-var) (+ pos step) end step) fold-var)) 为负,此解决方案仍然有效。如果step为正数,则if条件为step而不是嵌套if。

它还使用step您使用的(< pos end),但它也会切换if个案。如果(< pos end)(>= pos end)相同,则与您的解决方案相同,因为(< x y)等同于(not (>= x y))。但是,至少有一个案例,我可以想到哪些情况不成立,(if (not a) b c)(if a c b)start。对于这种情况,球拍的end函数将返回一个空列表,而您的解决方案将进入无限循环。

答案 1 :(得分:3)

它并不总是那么容易看到它,但是反过来&#34;这是一个关键字。为了使它迭代,你需要反过来做实际的工作:

(define (my-range start end step)      
  (define (helper n acc)
    (if (= end n)
        (cons n acc)
        (helper (- n step) (cons n acc))))

  (define actual-end end) ; this needs improvement
  (helper actual-end '()))

就像球拍中的range一样,你应该得到以下结果:

(my-range 1 10 1)  ; ==> (1 2 3 4 5 6 7 8 9)
(my-range 10 1 -1) ; ==> (10 9 8 7 6 5 4 3 2)

要获得正确的actual-end需要,请根据数学找到正确的值。

(my-range 1 10 2)  ; ==> (1 3 5 7 9)  (actual-end should be 9)
(my-range 10 1 -2) ; ==> (10 8 6 4 2) (actual-end should be 2)

我猜你可以使用ceiling程序和正常的数学程序来确保这一点。