2

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

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

4

2 回答 2

3

您发布的第一个代码片段确实是实现地图功能的一种方式。它使用一个命名的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


边注

#f除了(false) ,Scheme 中的所有内容都是真实的。例如:

(if (lambda (x) x) 
    1
    2)

if测试将评估为,1因为该if测试将检查是否(lambda (x) x)为真,即为真。它是一个 lambda。真值是在期望真值的表达式中计算为真的值(例如,ifcond)。


现在为您的条件。cond您将测试的第一个案例是否L为空。如果计算结果为#t,则返回空列表。这确实是正确的。在空列表上映射某些内容只是空列表。

第二种情况 ( (f (car L))) 字面意思是“如果f为真,则返回carL”。

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 的新手,所以这个函数就可以了。尝试并理解它。但是,有更好更快的方法来实现这种功能。阅读此页面以了解诸如累加器函数和尾递归之类的内容。我不会在这里详细介绍所有内容,因为它 1)不是问题,2)可能是信息过载。

于 2015-04-12T21:18:54.927 回答
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)
于 2016-03-10T03:53:28.903 回答