[英]Iterative map in scheme
我正在觀看SICP的視頻講座,並且我來到了一個部分,那里的老師正在顯示處理列表的過程,因此,其中一個是:
(define (map p l)
(if (null? l)
(list)
(cons (p (car l))
(map p (cdr l)))))
我想問的是:是否有一種以迭代方式定義map
的方法,或者cons
需要懶惰的評估才能正確執行?
您的原始代碼幾乎是尾部遞歸的..唯一使它不行的是cons
部分。 如果計划有平等的要求,對具有TRMC優化 ,因為它有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
可能是如何實現的。
您需要接受遞歸才能大致了解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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.