问题
Note: this is a bonus for homework, but I have spent way too long on trying things to no avail. Help is much appreciated, but not necessary I suppose.
Premise:
generate a powerset for a list of numbers, but without using any helpers, explicit recursion, looping, or functions/constants other than cons
, first
, rest
, empty?
, empty
, else
, lambda
, and cond
, while using only one define
on the language level Intermediate Student with Lambda
. The order of the powerset does not matter.
What I've tried so far:
I have discovered the Y-combinator and anonymous recursion thanks to this post (author has the same end goal but we have different approaches, so the information in his post does not solve my problem), and the powerset
code in this answer, and with that I have written the following:
(define (powerset aL)
(((lambda (X)
((lambda (proc)
(proc proc))
(lambda (proc)
(X (lambda (arg)
((proc proc) arg))))))
(lambda (subset)
(lambda (lst)
(cond
[(empty? lst) (list empty)]
[else (combine (first aL) (powerset (rest aL)))])))) aL)
(define (combine a r)
(cond
[(empty? r) empty]
[else (cons (cons a (first r)) (cons (first r) (combine a (rest r))))]))
I'm testing this code by running:
(check-expect (powerset '(1 2 3))
(list '(1 2 3) '(2 3) '(1 3) '(3) '(1 2) '(2) '(1) '()))
This code runs and produces a correct result, but, as you can see, I still rely on an external helper function, combine
, and I have no idea how to convert that into a lambda
since to my knowledge, the Y-combinator only works with one parameter and combine
needs 2. Perhaps my logic or approach to this problem is flawed. I have limited experience with lambda
so I could be missing knowledge as well.
What I need help with: any suggestions as to next steps, helping me intergrate combine
into powerset
, providing hints/clues to correct logic/approach, or a solution would be much appreciated.
Thanks in advance!
回答1:
I find the trick below easier to understand than using Y. I think it's sort of related to U (which I also find easier to understand than Y).
It's possible that this isn't enough to meet the requirement of 'not being explicitly recursive', although I think it is.
If you have some function which 'wants' to use itself freely so it can recurse, such as:
(define powerset
(λ (set)
(cond [(empty? set)
(list empty)]
[else
(combine (first set)
(powerset (rest set)))])))
Then you can turn that into a function which takes an additional argument, which it calls:
(define powerset/c
(λ (ps/c set)
(cond [(empty? set)
(list empty)]
[else
(combine (first set)
(ps/c ps/c (rest set)))])))
The /c
names are because when I discovered this trick I was thinking about the argument as a continuation, but I think that's because I didn't know what continuations were really.
And now (with a definition for combine
), (powerset/c powerset/c '(x y z))
will compute the power set of (x y z)
, and there is no explicit recursion.
Well, that's ugly but this is easy to fix using
(define powerset
(λ (set)
((λ (powerset/c)
(powerset/c powerset/c set))
(λ (ps/c set)
(cond [(empty? set)
(list empty)]
[else
(combine (first set)
(ps/c ps/c (rest set)))])))))
Then the trick is to write combine
this way as well, and then use it locally, with
(define powerset
(λ (set)
((λ (combine)
((λ (powerset/c)
(powerset/c powerset/c set))
(λ (ps/c set)
(cond [(empty? set)
(list empty)]
[else
(combine (first set)
(ps/c ps/c (rest set)))]))))
<combine defn here>)))
回答2:
the Y-combinator only works with one parameter and combine needs 2
Any multi-argument function can be imagined as a one-argument function, returning a lambda that waits for the next argument. This process is called currying. For example, if we have
(define add (x y)
(+ x y))
we could call it like
(add 2 2)
Simple enough. Now let's curry it:
(define (add x)
(lambda (y)
(+ x y)))
Calling it takes a slightly different syntax, but it's the same basic idea:
((add 2) 2)
You can apply the same concept to any lambda if you wish to make it suitable to the Y combinator.
回答3:
In lambda calculus, all functions are curried unary functions.
This means
(define (combine a r)
(cond
[(empty? r) empty]
[else (cons (cons a (first r))
(cons (first r)
(combine a (rest r))))]))
would be written as
(λ (combine)
(λ (a)
(λ (r)
(cond
[(empty? r) empty]
[else (cons (cons a (first r))
(cons (first r)
((combine a) (rest r))))]))))
With this in mind, here is the solution:
(define powerset
((λ (y)
((λ (f) (y (λ (x) ((f f) x))))
(λ (f) (y (λ (x) ((f f) x))))))
(λ (ps)
(λ (set)
(cond
[(empty? set) (cons empty empty)]
[else ((((λ (y)
((λ (f) (y (λ (x) ((f f) x))))
(λ (f) (y (λ (x) ((f f) x))))))
(λ (combine)
(λ (a)
(λ (r)
(cond
[(empty? r) empty]
[else (cons (cons a (first r))
(cons (first r)
((combine a) (rest r))))])))))
(first set))
(ps (rest set)))])))))
来源:https://stackoverflow.com/questions/64904835/generating-powerset-in-one-function-no-explicit-recursion-and-using-only-simpl