我对Scheme完全陌生,我正在尝试实现我自己的映射函数。我试着在网上找到它,但我遇到的所有问题都是关于一些复杂版本的映射函数(比如以两个列表作为输入的映射函数)。
我找到的最好的答案是:(对于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))))))
当用这种语言编程时,我确实理解函数方法背后的逻辑,但我在编码它时遇到了很大的困难
您发布的第一个代码片段实际上是实现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))))
正如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
。
旁注
Scheme中除了#f
(false)外,其他都是truthy。例如:
(if (lambda (x) x)
1
2)
这个if
测试将评估为1
,因为if
测试将检查(lambda (x) x)
是否是真的。它是一个lambda。真值是在期望真值的表达式中(例如if
和cond
)将评估为真的值。
现在是你的第二个。
cond
的第一种情况将测试L
是否为null。如果计算结果为#t
,则返回空列表。这确实是正确的。在空列表上映射某些内容就是空列表。第二种情况((f (car L))
)的字面意思是"如果f
为真,则返回L的car
"。
else
的情况说明"否则,返回我的列表L
的其余部分上的结果mymap
"。
我认为你真正想做的是使用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)可能是信息过载。
如果您正在实施自己的列表过程,您可能应该确保他们在可能的情况下使用正确的尾部调用
(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)