什么是#34;名为let"以及如何使用它来实现地图功能?

时间:2015-04-12 20:52:04

标签: functional-programming scheme map-function

我是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))))))

在使用这种语言进行编程时,我确实理解了函数式方法背后的逻辑,但是我编写它时遇到了很大的困难。

2 个答案:

答案 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)调用自身。这样你就可以做递归。有关letreclet 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值是在期望真值的表达式中将评估为真的值(例如,ifcond)。

<小时/> 现在为你的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)