问题
What happens when I do the following?
(define ((func x) y)
(if (zero? y)
((func x) 1)
12))
I understand that I can do this:
(define curried (func 5))
And now I can use curried. What I'm curious about is in the definition of the function. Does the line
((func x) 1)
create a new lambda with x as the argument, and then invoke it on 1? Or is it smarter than that and it just re-uses the existing one. (For example, if I do (curried 0)
, the ((func x) 1)
line would be equivalent to (curried 1)
- does PLAI Scheme do this?)
回答1:
In the Scheme standard it is specified that
(define (f x) 42) is short for (define f (lambda (x) 42)) .
The natural (non-standard) generalization implies:
(define ((f x) y) (list x y)) is short for (define (f x) (lambda (y) (list x y)))
which is short for (define f (lambda (x) (lambda (y) (list x y))))
To test it, let's try the example in DrScheme
Welcome to DrScheme, version 4.1.3.3-svn5dec2008 [3m]. Language: Module; memory limit: 384 megabytes.
(define ((f x) y) (list x y)) (f 1)
((f 1) 2) (1 2)
If we name the temporary value, it might be easier to see what happens:
(define h (f 1)) (h 2) (1 2) (h 3) (1 3)
Since "PLAI Scheme" is implemented in DrScheme, I believe it inherits this shortcut notation.
回答2:
It's been too long since I worked with scheme, but you might find this article helpful. It describes the implementation of two macros, c-lambda and c-define which allow implicit curried definitions of lambda expressions.
回答3:
soegaard's answer is correct - this is the traditional expansion. However, drscheme is smart!
The following code I've found to be equivalent in running time:
Original source:
(define ((substitute lv value) e)
(cond [(LogicVar? e)
(type-case LogicVar e
[lv-any (id) (if (symbol=? id (lv-any-id lv))
value
e)]
[lv-cons (f r)
(lv-cons ((substitute lv value) f)
((substitute lv value) r))])]
[(cons? e)
(cons ((substitute lv value) (car e))
((substitute lv value) (cdr e)))]
[else e]))
Attempt at optimization:
(define (substitute lv value)
(local ([define inner
(lambda (e)
(cond [(LogicVar? e)
(type-case LogicVar e
[lv-any (id) (if (symbol=? id (lv-any-id lv))
value
e)]
[lv-cons (f r)
(lv-cons (inner f)
(inner r))])]
[(cons? e)
(cons (inner (car e))
(inner (cdr e)))]
[else e]))])
inner))
Code which heavily uses this function (multiple times, not just once) runs at 1800 ms for both versions. More interestingly, this version (my visualization of what was happening):
(define (substitute lv value)
(local ([define inner
(lambda (e)
(cond [(LogicVar? e)
(type-case LogicVar e
[lv-any (id) (if (symbol=? id (lv-any-id lv))
value
e)]
[lv-cons (f r)
(lv-cons ((substitute lv value) f)
((substitute lv value) r))])]
[(cons? e)
(cons ((substitute lv value) (car e))
((substitute lv value) (cdr e)))]
[else e]))])
inner))
Runs at 2000 ms. So there is definitely a slow-down if the calls to substitute within substitute were each creating a lambda, but it appears this is not the case with the shortcut notation.
来源:https://stackoverflow.com/questions/357353/implementation-of-curried-functions-in-scheme