简体   繁体   English

什么是“命名为let”,以及如何使用它实现地图功能?

[英]What is “named let” and how do I use it to implement a map function?

I'm totally new to Scheme and I am trying to implement my own map function . 我对Scheme完全陌生, 正在尝试实现自己的map函数 I've tried to find it online, however all the questions I encountered were about some complex versions of map function (such as mapping functions that take two lists as an input). 我试图在网上找到它,但是我遇到的所有问题都是关于某些复杂版本的map函数(例如以两个列表作为输入的map函数)。

The best answer I've managed to find is here: ( For-each and map in Scheme ). 我设法找到的最佳答案是:( 对于Scheme中的for-each和map )。 Here is the code from this question: 这是此问题的代码:

(define (map func lst)
  (let recur ((rest lst))
    (if (null? rest)
      '()
      (cons (func (car rest)) (recur (cdr rest))))))

It doesn't solve my problem though because of the usage of an obscure function recur . 但是由于使用了模糊函数recur它不能解决我的问题。 It doesn't make sense to me. 对我来说这没有意义。

My code looks like this: 我的代码如下所示:

(define (mymap f L)
  (cond ((null? L) '())
    (f (car L))
    (else (mymap (f (cdr L))))))

I do understand the logic behind the functional approach when programming in this language, however I've been having great difficulties with coding it. 当我使用这种语言编程时,我的确理解了函数式方法背后的逻辑,但是我在编码方面一直遇到很大的困难。

The first code snippet you posted is indeed one way to implement the map function. 您发布的第一个代码片段确实是实现map功能的一种方法。 It uses a named let. 它使用一个命名的let。 See my comment on an URL on how it works. 请参阅我对URL的评论。 It basically is an abstraction over a recursive function. 它基本上是对递归函数的抽象。 If you were to write a function that prints all numbers from 10 to 0 you could write it liks this 如果要编写一个将所有数字从10打印到0的函数,可以这样写

(define (printer x)
  (display x)
  (if (> x 0)
      (printer (- x 1))))

and then call it: 然后调用它:

(printer 10)

But, since its just a loop you could write it using a named let: 但是,由于它只是一个循环,您可以使用命名的let来编写它:

(let loop ((x 10))
  (display x)
  (if (> x 0)
      (loop (- x 1))))

This named let is, as Alexis King pointed out, syntactic sugar for a lambda that is immediately called. 正如Alexis King所指出的那样,名为let的let是立即被调用的lambda的语法糖。 The above construct is equivalent to the snippet shown below. 上面的结构等同于下面显示的代码段。

(letrec ((loop (lambda (x) 
              (display x)
              (if (> x 0)
                  (loop (- x 1))))))
  (loop 10))

In spite of being a letrec it's not really special. 尽管是letrec但这并不特别。 It allows for the expression (the lambda, in this case) to call itself. 它允许表达式(在这种情况下为lambda)调用自身。 This way you can do recursion. 这样您可以进行递归。 More on letrec and let here . 更多关于letreclet 在这里

Now for the map function you wrote, you are almost there. 现在,对于您编写的map函数,您已经差不多了。 There is an issue with your two last cases. 您的后两种情况有问题。 If the list is not empty you want to take the first element, apply your function to it and then apply the function to the rest of the list. 如果列表不为空,则您要采用第一个元素,则将函数应用于该元素,然后将该函数应用于列表的其余部分。 I think you misunderstand what you actually have written down. 我认为您误会了您实际写下的内容。 Ill elaborate. 虐待详细。

Recall that a conditional clause is formed like this: 回想一下条件子句是这样形成的:

(cond (test1? consequence)
      (test2? consequence2)
      (else   elsebody))

You have any number of tests with an obligatory consequence. 您必须进行任何数量的测试并产生强制性结果。 Your evaluator will execute test1? 您的评估者将执行test1? and if that evaluated to #t it will execute the consequence as the result of the entire conditional. 如果将其评估为#t ,它将作为整个条件的结果执行结果。 If test1? 如果是test1? and test2? test2? fail it will execute elsebody . 失败它将执行elsebody


Sidenote 边注

Everything in Scheme is truthy except for #f (false). Scheme中的所有内容都是真实的,除了#f (false)。 For example: 例如:

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

This if test will evaluate to 1 because the if test will check if (lambda (x) x) is truthy, which it is. if if测试将检查(lambda (x) x)是否为真,则此if测试将评估为1 It is a lambda. 这是一个lambda。 Truthy values are values that will evaluate to true in an expression where truth values are expected (eg, if and cond ). 真值是在期望真值的表达式(例如ifcond )中将评估为真的值。


Now for your cond. 现在为您的cond。 The first case of your cond will test if L is null. cond的第一种情况将测试L是否为null。 If that is evaluated to #t , you return the empty list. 如果将其评估为#t ,则返回空列表。 That is indeed correct. 确实是正确的。 Mapping something over the empty list is just the empty list. 在空列表上映射某些内容只是空列表。

The second case ( (f (car L)) ) literally states "if f is true, then return the car of L". 第二种情况( (f (car L))字面意思是“如果f为真,则返回L的car ”。

The else case states "otherwise, return the result mymap on the rest of my list L ". else情况表明“否则,将结果mymap返回到列表L的其余部分”。

What I think you really want to do is use an if test. 我认为您真正想做的是使用if测试。 If the list is empty, return the empty list. 如果列表为空,则返回空列表。 If it is not empty, apply the function to the first element of the list. 如果不为空,则将该函数应用于列表的第一个元素。 Map the function over the rest of the list, and then add the result of applying the function the first element of the list to that result. 将函数映射到列表的其余部分,然后将应用函数的结果添加到列表的第一个元素中。

 (define (mymap f L) (cond ((null? L) '()) (f (car L)) (else (mymap (f (cdr L)))))) 

So what you want might look look this: 因此,您想要的内容可能看起来像这样:

 (define (mymap f L) (cond ((null? L) '()) (else (cons (f (car L)) (mymap f (cdr L)))))) 

Using an if : 使用if

 (define (mymap f L) (if (null? L) '() (cons (f (car L)) (mymap f (cdr L))))) 

Since you are new to Scheme this function will do just fine. 由于您是Scheme的新手,因此此功能就可以了。 Try and understand it. 尝试并了解它。 However, there are better and faster ways to implement this kind of functions. 但是,有更好,更快的方法来实现这种功能。 Read this page to understand things like accumulator functions and tail recursion. 阅读此页可了解累加器功能和尾部递归等内容。 I will not go in to detail about everything here since its 1) not the question and 2) might be information overload. 我不会在这里详细介绍所有内容,因为其1)不是问题,而2)可能是信息过载。

If you're taking on implementing your own list procedures, you should probably make sure they're using a proper tail call, when possible 如果要实施自己的列表过程,则应尽可能确保它们使用正确的尾部调用

(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)

Or you can make this a little sweeter with the named let expression, as seen in your original code. 或者,您可以使用命名的let表达式使它更加甜美,如您的原始代码所示。 This one, however, uses a proper tail call 但是,这使用了正确的尾声

(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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM