【发布时间】:2018-09-23 23:49:16
【问题描述】:
racket中map函数是如何实现的,为什么,递归还是迭代。
也许是一些实现示例
【问题讨论】:
-
看源码就行了...(map.rkt,在Dr.Racket右键,“打开定义文件”)
标签: racket
racket中map函数是如何实现的,为什么,递归还是迭代。
也许是一些实现示例
【问题讨论】:
标签: racket
map 函数遍历一个列表(或多个列表),并将给定函数应用于列表的每个值。例如将add1 映射到列表会导致:
> (map add1 '(1 2 3 4))
'(2 3 4 5)
因此,您可以将map 实现为递归函数:
(define (map func lst)
(if (empty? lst)
'()
(cons (func (first lst)) (map func (rest lst)))))
当然,map 可以接受任意数量的参数,每个元素都传递给给定的 prop。例如,您可以使用map list 将两个列表压缩在一起:
> (map list '(1 2 3) '(a b c))
'((1 a) (2 b) (3 c))
要实现这个可变的arity map,我们需要使用apply函数:
(define (map proc lst . lst*)
(if (empty? lst)
'()
(cons (apply proc (first lst) (map first lst*))
(apply map proc (rest lst) (map rest lst*)))))
现在,这确实假设所有给定的列表都具有相同的长度,否则你会得到一些意想不到的行为。要做到这一点,您需要在所有列表上运行empty?,而不仅仅是第一个。但是......当你使用它时,你会得到:
> (map list '(a b c) '(1 2 3))
'((a 1) (b 2) (c 3))
请注意,map 在这里递归调用自身 3 次。更快的实现可能会进行一些展开以更快地运行。更好的实现也会进行适当的错误检查,我在这个例子中省略了。
如果您打开 DrRacket(每晚使用最新的 Racket 7)并制作以下文件:
#lang racket
map
您现在可以右键单击map 并选择Open Defining File。从这里,您可以看到 map 是从定义 map2 重命名的。其中的定义是:
(define map2
(let ([map
(case-lambda
[(f l)
(if (or-unsafe (and (procedure? f)
(procedure-arity-includes? f 1)
(list? l)))
(let loop ([l l])
(cond
[(null? l) null]
[else
(let ([r (cdr l)]) ; so `l` is not necessarily retained during `f`
(cons (f (car l)) (loop r)))]))
(gen-map f (list l)))]
[(f l1 l2)
(if (or-unsafe
(and (procedure? f)
(procedure-arity-includes? f 2)
(list? l1)
(list? l2)
(= (length l1) (length l2))))
(let loop ([l1 l1] [l2 l2])
(cond
[(null? l1) null]
[else
(let ([r1 (cdr l1)]
[r2 (cdr l2)])
(cons (f (car l1) (car l2))
(loop r1 r2)))]))
(gen-map f (list l1 l2)))]
[(f l . args) (gen-map f (cons l args))])])
map))
【讨论】: