您发布的第一个代码片段确实是实现地图功能的一种方式。它使用一个命名的let。请参阅我对 URL 的评论,了解它是如何工作的。它基本上是对递归函数的抽象。如果您要编写一个打印从 10 到 0 的所有数字的函数,您可以这样写
(define (printer x)
(display x)
(if (> x 0)
(printer (- x 1))))
然后调用它:
(printer 10)
但是,由于它只是一个循环,您可以使用命名的 let 来编写它:
(let loop ((x 10))
(display x)
(if (> x 0)
(loop (- x 1))))
正如 Alexis King 所指出的,这个命名的 let 是立即调用的 lambda 的语法糖。上面的构造等效于下面显示的代码段。
(letrec ((loop (lambda (x)
(display x)
(if (> x 0)
(loop (- x 1))))))
(loop 10))
尽管是一个letrec
,但它并不是很特别。它允许表达式(在本例中为 lambda)调用自身。这样你就可以进行递归了。更多关于letrec
这里let
。
现在,对于您编写的 map 函数,您就快到了。你的最后两个案例有问题。如果列表不为空,您要获取第一个元素,请将您的函数应用于它,然后将该函数应用于列表的其余部分。我认为您误解了您实际写下的内容。不好详细说明。
回想一下,条件子句是这样形成的:
(cond (test1? consequence)
(test2? consequence2)
(else elsebody))
您可以进行任意数量的测试,这些测试具有强制性的后果。您的评估器将执行test1?
,如果对其评估,#t
它将执行结果作为整个条件的结果。如果失败test1?
,test2?
它将执行elsebody
。
边注
#f
除了(false) ,Scheme 中的所有内容都是真实的。例如:
(if (lambda (x) x)
1
2)
该if
测试将评估为,1
因为该if
测试将检查是否(lambda (x) x)
为真,即为真。它是一个 lambda。真值是在期望真值的表达式中计算为真的值(例如,if
和cond
)。
现在为您的条件。
cond
您将测试的第一个案例是否
L
为空。如果计算结果为
#t
,则返回空列表。这确实是正确的。在空列表上映射某些内容只是空列表。
第二种情况 ( (f (car L))
) 字面意思是“如果f
为真,则返回car
L”。
该else
案例指出“否则,将结果返回到mymap
我的列表的其余部分L
”。
我认为您真正想要做的是使用 if 测试。如果列表为空,则返回空列表。如果它不为空,则将该函数应用于列表的第一个元素。将该函数映射到列表的其余部分,然后将将该函数应用到列表的第一个元素的结果添加到该结果中。
(define (mymap f L)
(cond ((null? L) '())
(f (car L))
(else (mymap (f (cdr L))))))
所以你想要的可能看起来像这样:
(define (mymap f L)
(cond ((null? L) '())
(else
(cons (f (car L))
(mymap f (cdr L))))))
使用if
:
(define (mymap f L)
(if (null? L) '()
(cons (f (car L))
(mymap f (cdr L)))))
由于您是 Scheme 的新手,所以这个函数就可以了。尝试并理解它。但是,有更好更快的方法来实现这种功能。阅读此页面以了解诸如累加器函数和尾递归之类的内容。我不会在这里详细介绍所有内容,因为它 1)不是问题,2)可能是信息过载。