Simplify conditional expression - scheme

Is it possible to rewrite the expression below so that (foo) only appears once? As it stands it's not terrible, but these repeated calls to (foo) make the code hard to read.
(cond
(a
(x)
(foo))
(b
(y)
(foo))
(else
(z)
(bar)))

I'm not sure this is technically "answering your question," but I would disagree that the repeated calls to foo make the code "hard to read". Indeed, I would claim that they make it easier to read. Specifically, in the refactored versions, you have conditional branches with trailing code (the call to foo). This means that as a reader, I have to keep in mind while I'm reading the conditional the fact that there's more that happens later. Even more importantly, looking at the foo in the refactored code, we can't say for sure what code executed before this one; the original one didn't have this problem.
When large blocks of code are repeated, it makes code hard to read and fragile. When a single call is repeated, as here, it generally makes the code easier to read, especially if the function has a meaningful name.
Here, let me reindent that for you:
(cond
[a (x)
(foo)]
[b (y)
(foo)]
[else (z)
(bar)])

If by (foo) you mean something complicated, I suggest:
(let ()
(define (f) (foo))
(cond
(a (x) (f))
(b (y) (f))
(else (z) (f))))
or
(let ((f (lambda () (foo))))
(cond
(a (x) (f))
(b (y) (f))
(else (z) (f))))

Not necessarily more readable :) but this will work.
(cond ((or a b) ((cond (a (x)) (b (y))) (foo)) (else (z) (bar)))

Yes,
(cond
((cond
(a
(x)
#t)
(b
(y)
#t)
(else
(z)
#f)) (foo))
(else (bar)))
This does not introduce additional testing, in case a or b are complex expressions.

Yet another option:
(let ((myfoo (lambda (x) (x) (foo))))
(cond (a (myfoo x))
(b (myfoo y))
(else (z) (bar))))

Related

What is Classifier and Transformer in MIT Scheme?

In the following scheme code, accumulate does right-fold. When I tried to run using mit scheme. I ran into following error:
Transformer may not be used as an expression: #[classifier-item 13]
Classifier may not be used as an expression: #[classifier-item 12]
I google searched but didn't find useful information. Is it related a macro?
; This function is copied from SICP chapter 2
(define (accumulate op initial sequence)
(if (null? sequence)
initial
(op (car sequence)
(accumulate op initial (cdr sequence)))))
; works as expected
(accumulate
(lambda (x y) (or x y)) ; replace or with and also works
#f
'(#t #f #t #f #f)
))
; does not work
; error: Classifier may not be used as an expression: #[classifier-item 12]
(accumulate
or
#f
'(#t #f #t #f #f)
))
; does not work
; error: Transformer may not be used as an expression: #[classifier-item 13]
(accumulate
and
#f
'(#t #f #t #f #f)
))
Macros can be passed around in some languages, but not in Scheme and Common Lisp. The reason is that macros should be able to be expanded ahead of time. eg.
(define (cmp a b)
(cond ((< a b) -1)
((> a b) 1)
(else 0)))
Now a compiling Scheme will expand each node recursively replacing it with the expansion until it is no change:
(define (cmp a b)
(if (< a b)
(begin -1)
(cond ((> a b) 1)
(else 0))))
(define (cmp a b)
(if (< a b)
-1
(cond ((> a b) 1)
(else 0))))
(define (cmp a b)
(if (< a b)
-1
(if (> a b)
(begin 1)
(cond (else 0)))))
(define (cmp a b)
(if (< a b)
-1
(if (> a b)
1
(cond (else 0)))))
; end result
(define (cmp a b)
(if (< a b)
-1
(if (> a b)
1
0)))
From this point of cond doesn't need to exist in the underlying language at all since you'll never ever use it, but how would this have to be implemented to work:
(define (test syntax a b)
(syntax a b))
(test or #f #t)
For this to work the underlying language needs to know what or is even after expansion since syntax would need to be bound to or and then the transformation can happen. But when the code runs the macro expansion has already happened and in most implementations you would see something indicating that or is an unbound variable. It seems like MIT Scheme has added error checking for top level syntax syntax that will fire an error if you don't override it. Eg. if you add this you will not see any problems whatsoever:
(define (or a b) (if a a b))
(define (and a b) (if a b #f))
Now after those lines any reference to and and or are not the syntax, but these procedures. There are no reserved words in Scheme so if you do something crazy, like defining define you just cannot use it for the rest f that scope:
(define define display) ; defiens define as a top level variable
(define define) ; prints the representation of the function display
(define test 10) ; fail since test is an undefined variable so it cannot be displayed.
I created a interpreted lisp with macros that actually could be passed, but it isn't very useful and the chances of optimization is greatly reduced.
Yes it's related to the macros / special forms like and and or.
You can make it work simply by wrapping them as lambdas, (accumulate (lambda (a b) (or a b)) ...) -- the results will be correct but of course there won't be any short-circuiting then. The op is a function and functions receive their arguments already evaluated.
Either hide the arguments behind lambdas ((lambda () ...)) and evaluate them manually as needed, or define specific versions each for each macro op, like
(define (accumulate-or initial sequence)
(if (null? sequence)
initial
(or (car sequence)
(accumulate-or initial (cdr sequence)))))
Here sequence will still be evaluated in full before the call to accumulate-or, but at least the accumulate-or won't be working through it even after the result is already known.
If sequence contains some results of heavy computations which you want to avoid in case they aren't needed, look into using "lazy sequences" for that.

Scheme: are `letrec` and `letcc` crucial for efficiency?

I am reading The Seasoned Schemer by Friedman and Felleisen, but I am a little uneasy with some of their best practices.
In particular, the authors recommend:
using letrec to remove arguments that do not change for recursive applications;
using letrec to hide and to protect functions;
using letcc to return values abruptly and promptly.
Let's examine some consequences of these rules.
Consider for example this code for computing the intersection of a list of lists:
#lang scheme
(define intersectall
(lambda (lset)
(let/cc hop
(letrec
[[A (lambda (lset)
(cond [(null? (car lset)) (hop '())]
[(null? (cdr lset)) (car lset)]
[else (I (car lset) (A (cdr lset)))]))]
[I (lambda (s1 s2)
(letrec
[[J (lambda (s1)
(cond [(null? s1) '()]
[(M? (car s1) s2) (cons (car s1) (J (cdr s1)))]
[else (J (cdr s1))]))]
[M? (lambda (el s)
(letrec
[[N? (lambda (s)
(cond [(null? s) #f]
[else (or (eq? (car s) el) (N? (cdr s)))]))]]
(N? s)))]]
(cond [(null? s2) (hop '())]
[else (J s1)])))]]
(cond [(null? lset) '()]
[else (A lset)])))))
This example appears in Chapter 13 (not exactly like this: I glued the membership testing code that is defined separately in a previous paragraph).
I think the following alternative implementation, which makes very limited use of letrec and letcc is much more readable and simpler to understand:
(define intersectall-naive
(lambda (lset)
(letrec
[[IA (lambda (lset)
(cond [(null? (car lset)) '()]
[(null? (cdr lset)) (car lset)]
[else (intersect (car lset) (IA (cdr lset)))]))]
[intersect (lambda (s1 s2)
(cond [(null? s1) '()]
[(M? (car s1) s2) (cons (car s1) (intersect (cdr s1) s2))]
[else (intersect (cdr s1) s2)]))]
[M? (lambda (el s)
(cond [(null? s) #f]
[else (or (eq? (car s) el) (M? el (cdr s)))]))]]
(cond [(null? lset) '()]
[else (IA lset)]))))
I am new to scheme and my background is not in Computer Science, but it strikes me that we have to end up with such complex code for a simple list intersection problem. It makes me wonder how people manage the complexity of real-world applications.
Are experienced schemers spending their days deeply nesting letcc and letrec expressions?
This was the motivation for asking stackexchange.
My question is: are Friedman and Felleisen overly complicating this example for education's sake, or should I just get accustomed to scheme code full of letccs and letrecs for performance reasons?
Is my naive code going to be impractically slow for large lists?
I'm not an expert on Scheme implementations, but I have some ideas about what's going on here. One advantage the authors have via their let/cc that you don't have is early termination when it's clear what the entire result will be. Suppose someone evaluates
(intersectall-naive (list big-list
huge-list
enormous-list
gigantic-list
'()))
Your IA will transform this to
(intersect big-list
(intersect huge-list
(intersect enormous-list
(intersect gigantic-list
'()))))
which is reasonable enough. The innermost intersection will be computed first, and since gigantic-list is not nil, it will traverse the entirety of gigantic-list, for each item checking whether that item is a member of '(). None are, of course, so this results in '(), but you did have to walk the entire input to find that out. This process will repeat at each nested intersect call: your inner procedures have no way to signal "It's hopeless, just give up", because they communicate only by their return value.
Of course you can solve this without let/cc, by checking the return value of each intersect call for nullness before continuing. But (a) it is rather pretty to have this check only occur in one direction instead of both, and (b) not all problems will be so amenable: maybe you want to return something where you can't signal so easily that early exit is desired. The let/cc approach is general, and allows early-exit in any context.
As for using letrec to avoid repetition of constant arguments to recursive calls: again, I am not an expert on Scheme implementations, but in Haskell I have heard the guidance that if you are closing over only 1 parameter it's a wash, and for 2+ parameters it improves performance. This makes sense to me given how closures are stored. But I doubt it is "critical" in any sense unless you have a large number of arguments or your recursive functions do very little work: argument handling will be a small portion of the work done. I would not be surprised to find that the authors think this improves clarity, rather than doing it for performance reasons. If I see
(define (f a x y z)
(define (g n p q r) ...)
(g (g (g (g a x y z) x y z) x y z) x y z))
I will be rather less happy than if I see
(define (f a x y z)
(define (g n) ...)
(g (g (g (g a)))))
because I have to discover that actually p is just another name for x, etc., check that the same x, y, and z are used in each case, and confirm that this is on purpose. In the latter case, it is obvious that x continues to have that meaning throughout, because there is no other variable holding that value. Of course this is a simplified example and I would not be thrilled to see four literal applications of g regardless, but the same idea holds for recursive functions.

Maximum recursion error [duplicate]

I'm reading The Little Schemer. And thanks to my broken English, I was confused by this paragraph:
(cond ... ) also has the property of not considering all of its
arguments. Because of this property, however, neither (and ... ) nor
(or ... ) can be defined as functions in terms of (cond ... ), though
both (and ... ) and (or ... ) can be expressed as abbreviations of
(cond ... )-expressions:
(and a b) = (cond (a b) (else #f)
and
(or a b) = (cond (a #t) (else (b))
If I understand it correctly, it says (and ...) and (or ...) can be replaced by a (cond ...) expression, but cannot be defined as a function that contains (cond ...). Why is it so? Does it have anything to do with the variant arguments? Thanks.
p.s. I did some searching but only found that (cond ...) ignores the expressions when one of its conditions evaluate to #f.
Imagine you wrote if as a function/procedure rather than a user defined macro/syntax:
;; makes if in terms of cond
(define (my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))
;; example that works
(define (atom? x)
(my-if (not (pair? x))
#t
#f))
;; example that won't work
;; peano arithemtic
(define (add a b)
(my-if (zero? a)
b
(add (- a 1) (+ b 1))))
The problem with my-if is that as a procedure every argument gets evaluated before the procedure body gets executed. thus in atom? the parts (not (pair? x)), #t and #f were evaluated before the body of my-if gets executed.
For the last example means (add (- a 1) (+ b 1)) gets evaluated regardless of what a is, even when a is zero, so the procedure will never end.
You can make your own if with syntax:
(define-syntax my-if
(syntax-rules ()
((my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))))
Now, how you read this is the first part is a template where the predicate consequent and alternative represent unevaluated expressions. It's replaced with the other just reusing the expressions so that:
(my-if (check-something) (display 10) (display 20))
would be replaced with this:
(cond ((check-something) (display 10))
(else (display 20)))
With the procedure version of my-if both 10 and 20 would have been printed. This is how and and or is implemented as well.
You cannot define cond or and or or or if as functions because functions evaluate all their arguments. (You could define some of them as macros).
Read also the famous SICP and Lisp In Small Pieces (original in French).

"cond","and" and "or" in Scheme

I'm reading The Little Schemer. And thanks to my broken English, I was confused by this paragraph:
(cond ... ) also has the property of not considering all of its
arguments. Because of this property, however, neither (and ... ) nor
(or ... ) can be defined as functions in terms of (cond ... ), though
both (and ... ) and (or ... ) can be expressed as abbreviations of
(cond ... )-expressions:
(and a b) = (cond (a b) (else #f)
and
(or a b) = (cond (a #t) (else (b))
If I understand it correctly, it says (and ...) and (or ...) can be replaced by a (cond ...) expression, but cannot be defined as a function that contains (cond ...). Why is it so? Does it have anything to do with the variant arguments? Thanks.
p.s. I did some searching but only found that (cond ...) ignores the expressions when one of its conditions evaluate to #f.
Imagine you wrote if as a function/procedure rather than a user defined macro/syntax:
;; makes if in terms of cond
(define (my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))
;; example that works
(define (atom? x)
(my-if (not (pair? x))
#t
#f))
;; example that won't work
;; peano arithemtic
(define (add a b)
(my-if (zero? a)
b
(add (- a 1) (+ b 1))))
The problem with my-if is that as a procedure every argument gets evaluated before the procedure body gets executed. thus in atom? the parts (not (pair? x)), #t and #f were evaluated before the body of my-if gets executed.
For the last example means (add (- a 1) (+ b 1)) gets evaluated regardless of what a is, even when a is zero, so the procedure will never end.
You can make your own if with syntax:
(define-syntax my-if
(syntax-rules ()
((my-if predicate consequent alternative)
(cond (predicate consequent)
(else alternative)))))
Now, how you read this is the first part is a template where the predicate consequent and alternative represent unevaluated expressions. It's replaced with the other just reusing the expressions so that:
(my-if (check-something) (display 10) (display 20))
would be replaced with this:
(cond ((check-something) (display 10))
(else (display 20)))
With the procedure version of my-if both 10 and 20 would have been printed. This is how and and or is implemented as well.
You cannot define cond or and or or or if as functions because functions evaluate all their arguments. (You could define some of them as macros).
Read also the famous SICP and Lisp In Small Pieces (original in French).

double in scheme

How to write a program in scheme that takes an arbitrary
sexpression consisting of integers and which returns an sexpression that is identical to
the original but with all the integers doubled?
We want a procedure that takes an S-expression as input, and outputs an S-expression with the same structure, but where each integer is doubled; generally, a procedure to map S-expressions:
(define (double x)
(if (number? x)
(* x 2)
x)))
(define (sexp-map op sexp)
...)
(sexp-map double some-sexpression)
The S-expression we get (SEXP) is going to be either an atom, in which case the result is (OP SEXP), or a list of S-expressions. We might think to map OP across SEXP in this case, but S-expressions nest arbitrarily deep. What we should actually do is map a procedure that will transform each element in the smaller S-expression with OP. Well would you look at that, that's just another way to describe of the goal of the procedure we're currently trying to write. So we can map SEXP-MAP across SEXP.
Well, no we can't actually, because SEXP-MAP needs to be called with two arguments, and MAP will only give it the one. To get around that, we use a helper procedure F of one argument:
(define (sexp-map op sexp)
(define (f x)
(if (list? x)
(map f x)
(op x)))
(f sexp))
F winds up doing all the real work. SEXP-MAP is reduced to being a facade that's easier to use for the programmer.
It sounds like what you want is to find each integer in the s-expression, then double it, while keeping the rest of it the same.
If you're not aware, s-expressions are just lists that may happen to contain other lists, and it makes sense to deal with them in levels. For instance, here's a way to print all the values on level one of an s-expression:
(define (print-level-one sexp)
(display (car sexp))
(print-level-one (cdr sexp)))
This will end up calling display on the car of every part of the s-expression.
You could do something similar. You'll need the functions integer? and pair? to check whether something is an integer, which should be doubled, or another list, which should be treated just like the top-level list.
(Note: I'm being deliberately vague because of the comment about homework above. If you just want the answer, rather than help figuring out the answer, say so and I'll change this.)
An Sexp of numbers is one of
-- Number
-- ListOfSexp
A ListOfSexp is one of
-- empty
-- (cons Sexp ListOfSexp)
So, you'll need one function to handle both of those data definitions. Since the data definitions cross-reference each other, the functions will do the same. Each individual function is pretty straight forward.
(define (double E)
(cond ((null? E) '())
((list? (car E)) (cons (double (car E)) (double (cdr E))))
((number? E) (list E))
(else (cons (* 2 (car E)) (double (cdr E))))
))
(map (lambda (x) (* x 2)) '(1 2 3 4 5)) => '(2 4 6 8 10)
What does it do? (lambda (x) (* x 2)) takes a number and doubles it, and map applies that function to every element of a list.
Edit: Oh, but it won't go into trees.
(define double
(lambda (x)
(cond ((null? x) (list))
((list? (car x)) (cons (double (car x)) (double (cdr x))))
(else (cons (* 2 (car x)) (double (cdr x)))))))
EDIT
Fixed this. Thanks to Nathan Sanders for pointing out my initial oversight concerning nested lists.

Resources