什么是 "named let" 以及如何使用它来实现地图功能?
What is "named let" and how do I use it to implement a map function?
我是 Scheme 的新手,我正在尝试实现自己的地图功能。我试图在网上找到它,但是我遇到的所有问题都是关于 map 函数的一些复杂版本(例如将两个列表作为输入的映射函数)。
我找到的最佳答案在这里:(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))))))
我确实理解用这种语言编程时函数式方法背后的逻辑,但是我在编写代码时遇到了很大的困难。
您发布的第一个代码片段确实是实现地图功能的一种方式。它使用一个命名的 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
here.
现在,对于您编写的地图函数,您已经差不多完成了。你最后两个案例有问题。如果列表不为空,您想获取第一个元素,将您的函数应用于它,然后将该函数应用于列表的其余部分。我认为你误解了你实际写下的内容。懒得细说了。
回想一下,条件从句是这样构成的:
(cond (test1? consequence)
(test2? consequence2)
(else elsebody))
您有任意数量的测试具有强制性后果。您的评估程序将执行 test1?
,如果评估结果为 #t
,它将作为整个条件的结果执行结果。如果 test1?
和 test2?
失败,它将执行 elsebody
.
旁注
Scheme 中的所有内容都是真实的,除了 #f
(错误)。例如:
(if (lambda (x) x)
1
2)
此 if
测试将评估为 1
,因为 if
测试将检查 (lambda (x) x)
是否为真,事实确实如此。它是一个 lambda。真值是在期望真值的表达式中计算为真的值(例如,if
和 cond
)。
现在为你的条件。 cond
的第一个案例将测试 L
是否为空。如果计算结果为 #t
,则您 return 空列表。这确实是正确的。在空列表上映射一些东西只是空列表。
第二种情况((f (car L))
)字面意思是"if f
is true, then return the car
of L"。
else
案例表明 "otherwise, return the result mymap
on the rest of my list L
"。
我认为您真正想要做的是使用 if 测试。如果列表为空,return 为空列表。如果它不为空,则将函数应用于列表的第一个元素。将函数映射到列表的其余部分,然后将函数应用于列表第一个元素的结果添加到该结果。
(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) 可能是信息过载。
如果您正在着手实现自己的列表过程,您应该尽可能确保它们使用正确的尾调用
(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)
或者您可以使用 named 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)
我是 Scheme 的新手,我正在尝试实现自己的地图功能。我试图在网上找到它,但是我遇到的所有问题都是关于 map 函数的一些复杂版本(例如将两个列表作为输入的映射函数)。
我找到的最佳答案在这里:(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))))))
我确实理解用这种语言编程时函数式方法背后的逻辑,但是我在编写代码时遇到了很大的困难。
您发布的第一个代码片段确实是实现地图功能的一种方式。它使用一个命名的 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
here.
现在,对于您编写的地图函数,您已经差不多完成了。你最后两个案例有问题。如果列表不为空,您想获取第一个元素,将您的函数应用于它,然后将该函数应用于列表的其余部分。我认为你误解了你实际写下的内容。懒得细说了。
回想一下,条件从句是这样构成的:
(cond (test1? consequence)
(test2? consequence2)
(else elsebody))
您有任意数量的测试具有强制性后果。您的评估程序将执行 test1?
,如果评估结果为 #t
,它将作为整个条件的结果执行结果。如果 test1?
和 test2?
失败,它将执行 elsebody
.
旁注
Scheme 中的所有内容都是真实的,除了 #f
(错误)。例如:
(if (lambda (x) x)
1
2)
此 if
测试将评估为 1
,因为 if
测试将检查 (lambda (x) x)
是否为真,事实确实如此。它是一个 lambda。真值是在期望真值的表达式中计算为真的值(例如,if
和 cond
)。
现在为你的条件。
cond
的第一个案例将测试 L
是否为空。如果计算结果为 #t
,则您 return 空列表。这确实是正确的。在空列表上映射一些东西只是空列表。
第二种情况((f (car L))
)字面意思是"if f
is true, then return the car
of L"。
else
案例表明 "otherwise, return the result mymap
on the rest of my list L
"。
我认为您真正想要做的是使用 if 测试。如果列表为空,return 为空列表。如果它不为空,则将函数应用于列表的第一个元素。将函数映射到列表的其余部分,然后将函数应用于列表第一个元素的结果添加到该结果。
(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) 可能是信息过载。
如果您正在着手实现自己的列表过程,您应该尽可能确保它们使用正确的尾调用
(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)
或者您可以使用 named 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)