我正在观看SICP视频讲座,我来到一个导师正在展示使用列表的程序的部分,所以,这里有一个:
(define (map p l)
(if (null? l)
(list)
(cons (p (car l))
(map p (cdr l)))))
我想问的是:有没有办法以迭代的方式定义map
,或者cons
是否要求延迟评估才能正确执行?
答案 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)