Functional programming “什么是”呢;名为“let”;我如何使用它来实现映射功能?

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)))))) 但是,由

我对Scheme完全陌生,我正在尝试实现自己的映射功能。我试着在网上找到它,但是我遇到的所有问题都是关于map函数的一些复杂版本(例如以两个列表作为输入的mapping函数)

我找到的最好答案是:()。以下是此问题的代码:

(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
-表达式进行命名(即从外部),同时知道它自己的名称(即在内部)。但这当然是意见的问题。:)很公平!我同意。