Idiomatic usage of local vs lambda?
In Exercise 30.1.1 of HtDP, I started off using local
and then modified it to use lambda
in order to answer the question.
(define (add-to-each2 accu a-list)
(cond
[(empty? a-list) empty]
[else (local ((define s (+ a开发者_如何学Cccu (first a-list))))
(cons s (add-to-each2 s (rest a-list))))]))
and
(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)))]))
In this particular instance, to me, the local
version is easier to read. Are there situations where the lambda
version would be preferred? Thank you.
First off, I think you might be getting relative-2-absolute
confused with add-to-each
, since add-to-each
just adds the same number to each element of the list, rather than incrementing an accumulator. The rest of this post assumes that's the case, and just takes out that incrementing.
I think let
would be my first choice for the local binding. Your lambda
example uses a common pattern that simulates let
using lambda
and application:
(let ([x e]) body)
Is equivalent to:
((lambda (x) body) e)
If you use this transformation from lambda
to let
in your example, you get:
(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)))]))
A good compiler will likely generate the same code for this as for your two examples, so it mostly comes down to style. The "left-left lambda
" pattern can be more difficult to read, as you note, so I prefer let
.
However, Exercise 30.1.1 is trying to make you use map
as a replacement for the explicit recursion that occurs in each of your examples. You are using map
in your example but only for a single addition at a time, which makes map
kind of painful: why bother wrapping up (list (first y))
and (list x)
when you just want (+ (first y) x)
?
Let's look at a simple definition of map
to see how it might be helpful, rather than painful, for this problem:
(define (map f ls)
(cond
[(empty? ls) empty]
[else (cons (f (first ls)) (map f (rest ls)))]))
Right away, you should notice some similarities to add-to-each
: the first line of the cond
checks for empty, and the second line cons
es something to do with the first
element onto a recursive call to map
on the rest
. The key, then, is to pass map
an f
that does what you want to do to each element.
In the case of add-to-each
, you want to add a particular number to each element. Here's an example of adding 2
:
> (map (lambda (n) (+ 2 n)) (list 1 2 3 4 5))
(3 4 5 6 7)
Notice that map
and lambda
are both here as 30.1.1 requests, and they act on an entire list without the explicit recursion of the original add-to-each
: the recursion is all abstracted away in map
.
This should be enough to get you to a solution; I don't want to give away the final answer, though :)
精彩评论