本地vs lambda的习惯用法?

时间:2011-01-19 04:33:55

标签: scheme racket

Exercise 30.1.1 of HtDP中,我开始使用local,然后将其修改为使用lambda以回答问题。

(define (add-to-each2 accu a-list)
  (cond
    [(empty? a-list) empty]
    [else (local ((define s (+ accu (first a-list))))
            (cons s (add-to-each2 s (rest a-list))))]))

(define (add-to-each5 accu a-list)
  (cond
    [(empty? a-list) empty]
    [else (cons ((lambda (x y)
                   (first (map + (list (first y))
                               (list x)))) accu a-list)
                (add-to-each5 (+ accu (first a-list))(rest a-list)))]))

在这个特定的例子中,对我来说,local版本更容易阅读。是否存在lambda版本首选的情况?谢谢。

1 个答案:

答案 0 :(得分:3)

首先,我认为您可能会relative-2-absoluteadd-to-each混淆,因为add-to-each只是为列表的每个元素添加相同的数字,而不是递增累加器。这篇文章的其余部分假设是这种情况,并且只是取出那个增量。

我认为let将是我本地绑定的首选。您的lambda示例使用了使用let和应用程序模拟lambda的常见模式:

(let ([x e]) body)

相当于:

((lambda (x) body) e)

如果您在示例中使用从lambdalet的转换,则会得到:

(define (add-to-each5 n a-list)
  (cond
    [(empty? a-list) empty]
    [else (cons (let ([x n] [y a-list])
                  (first (map + (list (first y))
                              (list x))))
                (add-to-each5 n (rest a-list)))]))

一个好的编译器可能会为你的两个例子生成相同的代码,所以它主要归结为样式。正如您所注意到的,“左 - 左lambda”模式可能更难阅读,所以我更喜欢let

然而,练习30.1.1试图让你使用map来代替你的每个例子中出现的显式递归。您在示例中使用的是map,但一次只添加一次,这会让map感到痛苦:为什么当您刚刚结束(list (first y))(list x)时想要(+ (first y) x)

让我们看一下map的简单定义,看看它对这个问题有多大帮助,而不是痛苦:

(define (map f ls)
  (cond
    [(empty? ls) empty]
    [else (cons (f (first ls)) (map f (rest ls)))]))

马上,你应该注意到add-to-each的一些相似之处:cond的第一行检查为空,第二行cons与{{1}有关}}元素在first上对map的递归调用。因此,关键是将restmap传递给每个元素。

对于f,您希望为每个元素添加特定数字。以下是添加add-to-each

的示例
2

请注意> (map (lambda (n) (+ 2 n)) (list 1 2 3 4 5)) (3 4 5 6 7) map都在这里作为30.1.1请求,并且它们在整个列表上操作而没有原始lambda的显式递归:递归都是抽象的离开add-to-each

这应该足以让你找到解决方案;我不想放弃最后的答案,但是:)