计划中的迭代映射

时间:2014-03-28 13:04:43

标签: recursion scheme lisp iteration sicp

我正在观看SICP视频讲座,我来到一个导师正在展示使用列表的程序的部分,所以,这里有一个:

(define (map p l)
   (if (null? l) 
       (list)
       (cons (p (car l))
                    (map p (cdr l)))))

我想问的是:有没有办法以迭代的方式定义map,或者cons是否要求延迟评估才能正确执行?

2 个答案:

答案 0 :(得分:4)

你的原始代码几乎是尾递归的..唯一不成功的是cons部分。如果Scheme具有TRMC optimization的同等要求,因为它具有TCO要求,您可以将代码保留为原样,并且实现将使其为您递归递归。

由于这不是一项要求,我们需要进行自己的TRMC优化。通常在循环中迭代列表并使用累加器使其尾递归时,您会得到相反顺序的结果,因此您可以执行线性更新反向:

(define (map proc lst)
  (let loop ((lst lst) (acc '()))
    (cond ((null? lst) (reverse! acc) acc)
          (else (loop (cdr lst) 
                      (cons (proc (car lst)) acc))))))

或者你可以一次性完成所有这些:

(define (map proc lst)
  (define head (list 1))
  (let loop ((tail head) (lst lst))
    (cond ((null? lst) (cdr head))
          (else (set-cdr! tail (list (proc (car lst))))
                (loop (cdr tail) (cdr lst))))))

现在,在这两种情况下,您只改变过程自己创建的结构,因此对于用户来说,它也可能以与示例相同的方式实现。

当您在实施中使用map等更高阶的程序时,可能会发生这种情况。通过将提供的map的性能与具有很长列表的不同实现进行比较,可以很容易地找到它。执行之间的区别会告诉您它是否是TRMCO或者提供的map可能是如何实现的。

答案 1 :(得分:2)

你需要拥抱递归以便一般地欣赏SICP和Scheme,所以尽量习惯它,你会在以后的意愿中表示赞赏。

但是,你可以:

(define (iterative-map f lst)
  (define res null)
  (do ((i (- (length lst) 1) (- i 1))) ((= i -1))
    (set! res (cons (f (list-ref lst i)) res)))
  res)

(iterative-map (lambda (x) (+ x 1)) '(1 3 5))
=> '(2 4 6)

但如果可以避免,则使用set!被视为不良风格。

在Racket中,你有一组更优雅的循环:

(define (for-map f lst)
  (for/list ((i lst))
    (f i)))

(for-map add1 '(1 3 5))
=> '(2 4 6)