Local Procedure Bindings - syntax

I'm new to Scheme and Lisp in general, and upon learning I've stumbled upon a cryptic syntax used in local procedure binding:
(define mock
(lambda (s)
;; this is what I don't understand
(let splice ([l '()] [m (car s)] [r (cdr s)])
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))))
It took me a while to grasp that splice is a scoped procedure with 3 arities. Rewriting this in an ML-esque style it seems to produce similar output:
(define mock2
(lambda (s)
;; define `splice` first
(define splice
(lambda (la lb lc)
(append
(map (lambda (x) (cons lb x)) lc)
(if (null? lc) '()
(splice (cons lb la) (car lc) (cdr lc))))))
;; bind `splice` and its arguments together and call it with them
(let ([sp splice] [l '()] [m (car s)] [r (cdr s)])
(splice l m r))))
The second version is a bit longer and somewhat look more imperative, but defining splice as a normal procedure inside the scope before binding it in parallel with the arguments (or just chuck them in as-is) and calling it looks saner.
Question is are these two versions replaceable? If yes, could you help explain the first version's syntax of binding local variables (l, m, and r) within the splice binding form?

Calling splice is like re-entering a loop, which is what it is there for. A tail call is a goto anyway. It is often named loop, instead of thinking up some special name for it.
"looks saner" is debatable, and actually with Schemers you'll lose this one, because this is a very popular Scheme construct, called "named let". It is usually re-written with letrec btw, if/when one wants to rewrite it, to understand it better. Internal define can be used as well, but then, why not use (define (mock s) ... in the first place.
So, the usual way to re-write this
(define mock ; or: (define (mock s) ...
(lambda (s)
(let splice ([l '()] [m (car s)] [r (cdr s)])
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))))
is this:
(define mock
(lambda (s)
(letrec ([splice (lambda (l m r) ; or: (define (splice l m r) ...
(append
(map (lambda (x) (cons m x)) r)
(if (null? r) '()
(splice (cons m l) (car r) (cdr r)))))])
(splice '() (car s) (cdr s)))))
and writing it in the named let way saves one from having it defined in one place and called in another, potentially far away. A call enters its body from the start anyway, and named let better reflects that.
This is pretty self-explanatory. The transformation from one form to the other is purely syntactical, and both can be used interchangeably.

Related

Trying to append recursively, what am I doing wrong in Scheme?

Trying to recursively append every element from list a to list c. Currently only one elements gets appended. What am I doing wrong? I', trying to reverse a list. But currently stuck understanding how to even append a list to another list.
(define a '(1 2 3))
(define c '())
(define fun
(lambda (() l1) l1
(l l1) (append l1 (car l)) (fun (cdr l) l1)
))
(fun a c)
I'm using a scheme like interpreter, where I cannot use any new built-in functions except of the listfunctions.
You need to use
(lambda (l l1)
(if (null? l)
l1
(append l1 (car l)) (fun (cdr l) l1)))
what you wrote looks like it would work if there was a pattern matching facility, but our lambda is more basic and doesn't pattern match.

Scheme and Merge Sort?

I was assigned to write a merge sort in Scheme but I have some issues with it. I showed it my professor and he said there is one simple mistake. Can someone help me?
Plzz!
(define msort
(lamdba(1st)
(cond
((null?? 1st) 1st)
((null? (cdr 1st)) 1st)
(#t ((letrec ((half (quotient (lenght 1st) 2))
(merge (lamdba (a b result)
(cond ((null? a) (apped (reserve a) result))
((null? b) (append (reserve a) result))
((> (car a) (car b) (merge a (cdr b) (cons (car b) result))
(#t (merge (cdr a) b (cons (car a) result)))))))
(merge (msort (take 1st half)) (msort (drop 1st half)) '()))))))
One simple mistake? He probably referred to #1, but even after fixing that you have some identifiers and parenthesis to fix:
lambda, null?, length, append, and reverse is spelled incorrectly.
letrec result gets applied since you have excess parenthesis around it.
cond in merge where you compare elements are missing parenthesis two places.
It's obvious you need help with parenthesis matching so you should download a decent IDE to write code in. I use DrRacket for Scheme development (#!R5RS, #!R6RS and #!racket) and it idents (just press CTRL+i to get it reidented after pasting in code) and indicate where function names are written wrong when you hit RUN.
Making merge a global function in the beginning and perhaps move it to a letrec later (if you have to) might ease development. Eg. you could find errors by testing stuff like (merge '(3 2 1) '()).
This is no guarantee the program will work since I only address syntax here. You need to debug it! DrRacket has a debugger too!
I think it is useful to implement first a function that allow to merge two ordered lists:
(define (merge l1 l2)
(if (empty? l1)
l2
(if (empty? l2)
l1
(if (< (car l1) (car l2))
(cons (car l1) (merge (cdr l1) l2))
(cons (car l2) (merge l1 (cdr l2)))))))
Now assume we have a function (get ls pos) capable to return the element of ls in position pos:
(define (get ls pos)
(if (= pos 1)
(car ls)
(get (cdr ls) (- pos 1))))
Finally, we can implement mergesort function:
(define (mergesort l p r)
(if (= p r)
(cons (get l p) empty)
(merge (mergesort l p (floor (/ (+ p r) 2))) (mergesort l (+ (floor (/ (+ p r) 2)) 1) r))))

Flatten once procedure

I'm having a bit of a struggle with coding a procedure that flattens a list once, i.e
(flatten-once '((b) (c f) ((d)(e)))) would produce '(b c f (d) (e))). I checked up on a few sources on how the standard flatten procedure works but it implements functions that are not included in the intermediate student with lambda language form I'm required to use. As far as I have it figured out a foldr would be somewhat helpful and have managed to get this
(define (flatten-once lst)
(cond
[(empty? lst) lst]
[else
((foldr cons (first (rest lst)) (first lst)))]))
which returns '(b c f) , so I guess it flattens part of the list. I tried continuing the definition through recursion but that just gives errors, so I guess I'm missing something.
The proposed code is overly complicated, resist the temptation to use folds everywhere, they're not the answer to everything - I say this because I've seen other of your questions and frequently, there's an unnecessary call to foldr or foldl. A simple append will do the trick:
(define (flatten-once lst)
(apply append lst))
It works as expected:
(flatten-once '((b) (c f) ((d)(e))))
=> '(b c f (d) (e))
If the input list contains elements which are not lists, then a bit more of work needs to be done. To be extra-careful, we can do this:
(define (flatten-once lst)
(apply append
(map (lambda (e) (if (cons? e) e (list e)))
lst)))
Now it'll work for inputs such as this one, noticing that the element a was added to the list. Another alternative would be to delete it from the list, if that makes more sense then replace (list e) with '() in the code above.
(flatten-once '(a (b) (c f) ((d)(e))))
=> '(a b c f (d) (e))
Finally, in the spirit of #Alex's answer, this second variant can also be written using foldr:
(define (flatten-once lst)
(foldr (lambda (e acc)
(append (if (cons? e) e (list e)) acc))
'()
lst))
The trick is as Óscar López already said, to use append.
If you want to use a solution with foldr :
(define (flatten-once lst)
(foldr append '() lst))
It should work as expected.
Less elegant that Oscar's version, but without append (and twice as fast in my tests):
(define (flatten-once lst)
(reverse
(let loop ((lst lst) (first #t) (res null))
(if (null? lst)
res
(let ((e (car lst)))
(loop (cdr lst)
first
(if (and first (cons? e))
(loop e #f res)
(cons e res))))))))
NB in intermediate student with lambda language this needs to be expressed as:
(define (flatten-once-helper lst first res)
(if (null? lst)
res
(let ((e (car lst)))
(flatten-once-helper
(cdr lst)
first
(if (and first (cons? e))
(flatten-once-helper e #f res)
(cons e res))))))
(define (flatten-once lst)
(reverse (flatten-once-helper lst #t null)))

Scheme: when are expressions in let evaluated?

On page 66 of "The Seasoned Schemer" it says that (let ...) is an abbreviation for :
(let ((x1 a1) ... (xn an)) b ...) = ((lambda (x1 ... xn) b ...) a1 ... an)
Its used on for example on page 70:
(define depth*
(lambda (l)
(let ((a (add1 (depth* (car l))))
(d (depth* (cdr l))))
(cond
((null? l) 1)
((atom? (car l)) d)
(else (cond
((> d a) d)
(else a)))))))
But that above definition of lambda would suggest that (add1 (depth* (car l)) and (depth* (cdr l)) are evaluated and passed into the lambda represented by (lambda (x1 ... xn) b ...). But this would mean that the list l, which could potentially be empty, would be passed to car and cdr before the null check in (null? l) 1) is ever done.
You're right in stating that (car l) and (cdr l) will get executed before testing if l is null, therefore raising an error if l is indeed null. Keep reading the book, in the following two pages this is explained, and a correct version of depth* is shown.
The let syntactic keyword accepts the following form (ignore 'named-let'):
(define-syntax let
(syntax-rules ()
((let ((identifier expression) ...) body ...)
;; ...)))
At the point where let is used each of expression ... is evaluated. The expressions are evaluated in an unspecified order.
In your case, the expressions for a and d involving depth* will be evaluated before the body. Thus, as you've concluded, l could be '() when car and cdr are invoked.

Y combinator discussion in "The Little Schemer"

So, I've spent a lot of time reading and re-reading the ending of chapter 9 in The Little Schemer, where the applicative Y combinator is developed for the length function. I think my confusion boils down to a single statement that contrasts two versions of length (before the combinator is factored out):
A:
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0 )
(else (add1
((mk-length mk-length)
(cdr l))))))))
B:
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length))))
Page 170 (4th ed.) states that A
returns a function when we applied it to an argument
while B
does not return a function
thereby producing an infinite regress of self-applications. I'm stumped by this. If B is plagued by this problem, I don't see how A avoids it.
Great question. For the benefit of those without a functioning DrRacket installation (myself included) I'll try to answer it.
First, let's use some sane (short) variable names, easily trackable by a human eye/mind:
((lambda (h) ; A.
(h h)) ; apply h to h
(lambda (g)
(lambda (lst)
(if (null? lst) 0
(add1
((g g) (cdr lst)))))))
The first lambda term is what's known as little omega, or U combinator. When applied to something, it causes that term's self-application. Thus the above is equivalent to
(let ((h (lambda (g)
(lambda (lst)
(if (null? lst) 0
(add1 ((g g) (cdr lst))))))))
(h h))
When h is applied to h, new binding is formed:
(let ((h (lambda (g)
(lambda (lst)
(if (null? lst) 0
(add1 ((g g) (cdr lst))))))))
(let ((g h))
(lambda (lst)
(if (null? lst) 0
(add1 ((g g) (cdr lst)))))))
Now there's nothing to apply anymore, so the inner lambda form is returned — along with the hidden linkages to the environment frames (i.e. those let bindings) up above it.
This pairing of a lambda expression with its defining environment is known as closure. To the outside world it is just another function of one parameter, lst. No more reduction steps left to perform there at the moment.
Now, when that closure — our list-length function — will be called, execution will eventually get to the point of (g g) self-application, and the same reduction steps as outlined above will again be performed (recalculating the same closure). But not earlier.
Now, the authors of that book want to arrive at the Y combinator, so they apply some code transformations to the first expression, to somehow arrange for that self-application (g g) to be performed automatically — so we may write the recursive function application in the normal manner, (f x), instead of having to write it as ((g g) x) for all recursive calls:
((lambda (h) ; B.
(h h)) ; apply h to h
(lambda (g)
((lambda (f) ; 'f' to become bound to '(g g)',
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst)))))) ; here: (f x) instead of ((g g) x)!
(g g)))) ; (this is not quite right)
Now after few reduction steps we arrive at
(let ((h (lambda (g)
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(g g)))))
(let ((g h))
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(g g))))
which is equivalent to
(let ((h (lambda (g)
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(g g)))))
(let ((g h))
(let ((f (g g))) ; problem! (under applicative-order evaluation)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))))
And here comes trouble: the self-application of (g g) is performed too early, before that inner lambda can be even returned, as a closure, to the run-time system. We only want it to be reduced when the execution gets to that point inside the lambda expression, after the closure was called. To have it reduced before the closure is even created is ridiculous. A subtle error. :)
Of course, since g is bound to h, (g g) is reduced to (h h) and we're back again where we started, applying h to h. Looping.
Of course the authors are aware of this. They want us to understand it too.
So the culprit is simple — it is the applicative order of evaluation: evaluating the argument before the binding is formed of the function's formal parameter and its argument's value.
That code transformation wasn't quite right, then. It would've worked under normal order where arguments aren't evaluated in advance.
This is remedied easily enough by "eta-expansion", which delays the application until the actual call point: (lambda (x) ((g g) x)) actually says: "will call ((g g) x) when called upon with an argument of x".
And this is actually what that code transformation should have been in the first place:
((lambda (h) ; C.
(h h)) ; apply h to h
(lambda (g)
((lambda (f) ; 'f' to become bound to '(lambda (x) ((g g) x))',
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst)))))) ; here: (f x) instead of ((g g) x)
(lambda (x) ((g g) x)))))
Now that next reduction step can be performed:
(let ((h (lambda (g)
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(lambda (x) ((g g) x))))))
(let ((g h))
(let ((f (lambda (x) ((g g) x)))) ; here it's OK
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))))
and the closure (lambda (lst) ...) is formed and returned without a problem, and when (f (cdr lst)) is called (inside the closure) it is reduced to ((g g) (cdr lst)) just as we wanted it to be.
Lastly, we notice that (lambda (f) (lambda (lst ...)) expression in C. doesn't depend on any of the h and g. So we can take it out, make it an argument, and be left with ... the Y combinator:
( ( (lambda (rec) ; D.
( (lambda (h) (h h))
(lambda (g)
(rec (lambda (x) ((g g) x)))))) ; applicative-order Y combinator
(lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst)))))) )
(list 1 2 3) ) ; ==> 3
So now, calling Y on a function is equivalent to making a recursive definition out of it:
( y (lambda (f) (lambda (x) .... (f x) .... )) )
=== define f = (lambda (x) .... (f x) .... )
... but using letrec (or named let) is better — more efficient, defining the closure in self-referential environment frame. The whole Y thing is a theoretical exercise for the systems where that is not possible — i.e. where it is not possible to name things, to create bindings with names "pointing" to things, referring to things.
Incidentally, the ability to point to things is what distinguishes the higher primates from the rest of the animal kingdom ⁄ living creatures, or so I hear. :)
To see what happens, use the stepper in DrRacket.
The stepper allows you to see all intermediary steps (and to go back and forth).
Paste the following into DrRacket:
(((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0 )
(else (add1
((mk-length mk-length)
(cdr l))))))))
'(a b c))
Then choose the teaching language "Intermediate Student with lambda".
Then click the stepper button (the green triangle followed by a bar).
This is what the first step looks like:
Then make an example for the second function and see what goes wrong.

Resources