Functional programming “什么是”呢;名为“let”;我如何使用它来实现映射功能?
我对Scheme完全陌生,我正在尝试实现自己的映射功能。我试着在网上找到它,但是我遇到的所有问题都是关于map函数的一些复杂版本(例如以两个列表作为输入的mapping函数) 我找到的最好答案是:()。以下是此问题的代码:Functional programming “什么是”呢;名为“let”;我如何使用它来实现映射功能?,functional-programming,scheme,map-function,Functional Programming,Scheme,Map Function,我对Scheme完全陌生,我正在尝试实现自己的映射功能。我试着在网上找到它,但是我遇到的所有问题都是关于map函数的一些复杂版本(例如以两个列表作为输入的mapping函数) 我找到的最好答案是:()。以下是此问题的代码: (define (map func lst) (let recur ((rest lst)) (if (null? rest) '() (cons (func (car rest)) (recur (cdr rest)))))) 但是,由
(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))))))
(cond (test1? consequence)
(test2? consequence2)
(else elsebody))
在用这种语言编程时,我确实理解函数方法背后的逻辑,但是我在编写代码时遇到了很大的困难。您发布的第一个代码片段确实是实现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的语法糖,lambda立即被称为。上面的构造相当于下面显示的代码段
(letrec ((loop (lambda (x)
(display x)
(if (> x 0)
(loop (- x 1))))))
(loop 10))
尽管它是一个letrec
,但它并不特别。它允许表达式(在本例中为lambda)调用自身。这样您就可以执行递归。有关letrec
和let
的详细信息
现在,对于您编写的map函数,您就快到了。你最后的两个案子有问题。如果列表不为空,则要获取第一个元素,请将函数应用于该元素,然后将函数应用于列表的其余部分。我想你误解了你实际上写的东西。我没有详细说明
回想一下,条件从句是这样形成的:
(define (mymap f L)
(cond ((null? L) '())
(f (car L))
(else (mymap (f (cdr L))))))
(cond (test1? consequence)
(test2? consequence2)
(else elsebody))
你可以进行任意数量的测试,测试结果是必须的。您的求值器将执行test1?
,如果求值结果为#t
,它将执行整个条件测试的结果。如果test1?
和test2?
失败,它将执行elsebody
旁注 方案中的所有内容都是真实的,除了
#f
(false)。例如:
(if (lambda (x) x)
1
2)
此if
测试将评估为1
,因为if
测试将检查(lambda(x)x)
是否真实,即是否真实。这是一辆兰巴。Truthy值是在期望真值的表达式中计算为真的值(例如,if
和cond
)
现在是你的条件。
cond
的第一个案例将测试L
是否为空。如果计算结果为#t
,则返回空列表。这的确是正确的。映射空列表上的内容就是空列表
第二种情况((f(car L))
)字面上表示“如果f
为真,则返回L的car
”
else
案例说明“否则,返回我列表其余部分的结果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))))))
如果,则使用:
(define (mymap f L)
(if (null? L) '()
(cons (f (car L))
(mymap f (cdr L)))))
因为你是新的方案,这个功能会做得很好。试着去理解它。然而,有更好更快的方法来实现这类功能。阅读以了解累加器函数和尾部递归之类的内容。我不会在这里详细介绍每件事,因为它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)
recur
是一个命名let。这包含了理解它所需的所有信息:我想说,letrec
真的很特别,因为它允许对lambda
-表达式进行命名(即从外部),同时知道它自己的名称(即在内部)。但这当然是意见的问题。:)很公平!我同意。