我是Scheme的新手,我正在尝试实现自己的地图功能。我试图在网上找到它,但是我遇到的所有问题都是关于地图功能的一些复杂版本(例如以两个列表作为输入的映射函数)。
我设法找到的最佳答案是:(For-each and map in Scheme)。以下是此问题的代码:
(define (map func lst)
(let recur ((rest lst))
(if (null? rest)
'()
(cons (func (car rest)) (recur (cdr rest))))))
虽然因为使用了一个模糊的函数recur
,但它并没有解决我的问题。这对我没有意义。
我的代码如下所示:
(define (mymap f L)
(cond ((null? L) '())
(f (car L))
(else (mymap (f (cdr L))))))
在使用这种语言进行编程时,我确实理解了函数式方法背后的逻辑,但是我编写它时遇到了很大的困难。
答案 0 :(得分:3)
您发布的第一个代码段实际上是实现map函数的一种方法。它使用一个名为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))))
正如亚历克西斯·金所指出的那样,这个名为let的是一个立即调用的lambda的语法糖。上面的结构等同于下面的代码片段。
(letrec ((loop (lambda (x)
(display x)
(if (> x 0)
(loop (- x 1))))))
(loop 10))
尽管是letrec
,但它并不特别。它允许表达式(在本例中为lambda)调用自身。这样你就可以做递归。有关letrec
和let
here的更多信息。
现在你写的地图功能,你几乎就在那里。你的最后两个案件存在问题。如果列表不为空,则需要获取第一个元素,将函数应用于该元素,然后将该函数应用于列表的其余部分。我想你误解了你实际记下的内容。生病了。
回想一下条件子句是这样形成的:
(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。 Truthy值是在期望真值的表达式中将评估为真的值(例如,if
和cond
)。
<小时/> 现在为你的cond。
cond
的第一种情况将测试L
是否为空。如果将其评估为#t
,则返回空列表。这确实是正确的。在空列表上映射某些内容只是空列表。
第二种情况((f (car L))
)按字面说明“如果f
为真,则返回L的car
”。
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的新手,这个功能会很好。尝试并理解它。但是,有更好,更快的方法来实现这种功能。阅读this page以了解累加器函数和尾递归等内容。我不会详细介绍这里的所有内容,因为1)不是问题,2)可能是信息过载。
答案 1 :(得分:1)
如果你正在实施自己的列表程序,你可能应该确保他们在可能的情况下使用正确的尾部调用
(define (map f xs)
(define (loop xs ys)
(if (empty? xs)
ys
(loop (cdr xs) (cons (f (car xs)) ys))))
(loop (reverse xs) empty))
(map (λ (x) (* x 10)) '(1 2 3 4 5))
; => '(10 20 30 40 50)
或者您可以使用名为let 的表达式使其更甜一些,如原始代码中所示。但是,这个使用正确的尾调用
(define (map f xs)
(let loop ([xs (reverse xs)] [ys empty])
(if (empty? xs)
ys
(loop (cdr xs) (cons (f (car xs)) ys)))))
(map (λ (x) (* x 10)) '(1 2 3 4 5))
; => '(10 20 30 40 50)