this is the original form:
(define (split-by l p k)
(let loop ((low '())
(high '())
(l l))
(cond ((null? l)
(k low high))
((p (car l))
(loop low (cons (car l) high) (cdr l)))
(else
(loop (cons (car l) low) high (cdr l))))))
and i'm trying to convert let, this is what I have tried:
(define (split-by l p k)
(lambda (loop)
(cond ((null? l) (k low high))
((p (car l))
(loop low (cons (car l) high) (cdr l)))
(else
(loop (cons (car l) low) high (cdr l))
((low '()) (high '()) (l l))))))
I don't know how to fix this so if anyone could help what I am doing wrong would be a great help.
If I understand correctly what you're doing, p
is a predicate and you split the list l
according to this, aggregating your two resulting lists with the aggregation function k
; in pseudo-code:
(split-by l p k) => (k {x in l | !p(x)} {x in l | p(x)})
The problem when replacing your let
is that the loop
function is recursively defined. It is of the form:
(define (loop low high lst)
(cond
((null? lst) <some value>)
(<some predicate> (loop (cons (car lst) low) high (cdr lst)))
(else (loop low (cons (car lst) high) (cdr lst)))))
You absolutely can use this directly in your function, defining the 'inner' recursive part, but this cannot be made using simple lambda
without let
: the function needs to refer to itself (since it is recursive), and you can only do that by assigning it a name. define
will do that, let
will let you do that, but no matter how you turn it, you need that self-reference. If you're clever and pass in a continuation:
(lambda (low high lst cont)
(cond
((null? lst) (agg high lst))
((pred? (car lst)) (cont low (cons (car lst) high) (cdr lst) cont))
(else (cont (cons (car lst) low) high (cdr lst) cont))))
You've removed that self-reference by making it explicit, but what do you pass as cont
? Well, if you assigned that via a let, you have a symbol refering to it:
(define (split-by2 lst pred? agg)
(let ((f (lambda (low high lst cont)
(cond
((null? lst) (agg low high))
((pred? (car lst)) (cont low (cons (car lst) high) (cdr lst) cont))
(else (cont (cons (car lst) low) high (cdr lst) cont))))))
(f '() '() lst f)))
Or more concisely with a define
, which does the exact same thing (without the need to pass in the continuation):
(define (split-by3 lst pred? agg)
(define (f low high lst)
(cond
((null? lst) (agg low high))
((pred? (car lst)) (f low (cons (car lst) high) (cdr lst)))
(else (f (cons (car lst) low) high (cdr lst)))))
(f '() '() lst))
All of them operate similarly:
(split-by '(1 2 3 4) (lambda (x) (> x 2)) list)
=> ((2 1) (4 3))
(split-by2 '(1 2 3 4) (lambda (x) (> x 2)) list)
=> ((2 1) (4 3))
(split-by3 '(1 2 3 4) (lambda (x) (> x 2)) list)
=> ((2 1) (4 3))
But you cannot get away with defining a symbol for your recursive function*.
As to why your example didn't work, it's working perfectly fine, except that it creates a function, taking as argument a function (which I called cont
above) and applying your logic given that function loop
. Since you then don't have any 'loop' to pass it (as you haven't bound it), it returns that function and proceeds to do nothing (in addition, in your returned lambda
, low
and high
are not defined).
* This is not entirely true as you could do it using combinators on your lambda, but that would make it much more complex than it ought to:
(define Y
(lambda (h)
((lambda (x) (x x))
(lambda (g)
(h (lambda args (apply (g g) args)))))))
(define (split-ycomb lst pred? agg)
((Y
(lambda(f)
(lambda (low high l)
(cond
((null? l) (agg low high))
((pred? (car l)) (f low (cons (car l) high) (cdr l)))
(else (f (cons (car l) low) high (cdr l)))))))
'() '() lst))
Or for an even uglier purer version, with an inline combinator:
(define (split-ycomb2 lst pred? agg)
(((lambda (h)
((lambda (x) (x x))
(lambda (g)
(h (lambda args (apply (g g) args))))))
(lambda(f)
(lambda (low high l)
(cond
((null? l) (agg low high))
((pred? (car l)) (f low (cons (car l) high) (cdr l)))
(else (f (cons (car l) low) high (cdr l)))))))
'() '() lst))
Which work as expected (thanks to the layers of lambdas):
(split-ycomb '(1 2 3 4) (lambda (x) (> x 2)) list)
=> ((2 1) (4 3))
(split-ycomb2 '(1 2 3 4) (lambda (x) (> x 2)) list)
=> ((2 1) (4 3))