Reusing values in condition and consequent for efficiency in lisp / clojure - performance

I have a cond, e.g of the form:
(cond
(condition1) (consequent1)
(condition2) (consequent2))
Say in condition2 I want to compute some value which is expensive, so I would prefer to only do it once. If condition2 is true then I would like to use this expensive value in consequent2. My dilemma is then that I don't want to recompute the value in the condition and consequent, since this is wasteful. I also don't want to throw the whole cond inside a larger let function, e.g.
(let [value-used-in-cond2 do-expensive-computation]
(cond
(condition1) (consequent1)
(condition2) (consequent2)))
since I don't want to compute this value if I never get to condition 2, i.e. if condition1 is true.
Is there an idiomatic way to deal with this? The first thing that comes to mind is to memoizing the expensive function, but there must be simpler solution.

In On Lisp, Paul Graham describes macros for anaphoric variants of Common Lisp conditionals which bind the symbol 'it to the value of the condition expression. These macros obey the same evaluation semantics as the normal conditional forms, so from your example, condition2 will be evaluated after condition1 and only if condition1 is false. All conditions will be evaluated at most once. You can download On Lisp at http://www.paulgraham.com/onlisptext.html, and the code for the anaphoric macros is in figure 14.1 on page 191.

A somewhat ugly solution that should work in Clojure is
(let [expensive-result (or (condition1) (do-expensive-computation)]
(cond (condition1) (consequent1)
(condition2) (consequent2)))
This however requires condition1 to be evaluated twice.
Assuming that lisp / clojure in the heading means Clojure or (another) lisp,
in Common Lisp you can do
(let (var)
(cond
((condition1) (consequent1))
((setq var (condition2)) (consequent2))))
but this will not work in Clojure at the local variable is immutable.
You can use an atom to accomplish something similar with Clojure.
(let [v (atom nil)]
(cond
(condition1) (consequent1)
(do (reset! v (expensive)) (condition2 #v)) (consequent2 #v)))

Use a delay to compute something at most once and use it zero or more times:
(let [expensive-thing (delay do-expensive-computation)]
(cond (condition1) (consequent1)
(condition2 #expensive-thing) (consequent2 #expensive-thing)))

One way of rewriting this in Clojure to avoid repeating the computation would be:
(or
(when (condition1) (consequent1))
(when-let [val2 (condition2)] (consequent2 val2)))
This works assuming that consequent1 and consequent2 never return nil - otherwise evaluation of the or would fall through to the next form.

I had a similar problem, but I only have two case condition so I used a combination of a function and the if-let macro so:
(defn- expensive-computation
[a b]
(if (test (compute a b)) a nil))
(if-let [foo (expensive-computation a b)]
(consequent2 foo)
(consequent1))
As you can see the value is only computed once, after that a simple comparison is done to check if the test of the computation was sucessfull or not. If the test was not sucessful then it return nil, thus executing consequent1.
It is not a super clean solution but it was the best that I found. Hope it helps

If condition2 uses an expensive value, and yet condition2 is false, I assume that it is of the form
(and (expensive-calculation) (other-extra-condition))
What I do in these cases is:
(let (expensive-value)
(cond (condition1 consequent1)
(and (setq expensive-value (expensive-calculation)) (other-extra-condition)) consequent2)
((and expensive-value (other-extra-condition2)) consequent3)

Related

Is there normal "set" function (not special form) in Scheme language?

Is there normal "set" function (not special form) in Scheme language or some way to implement it?
I'd like to write code something like:
(map (lambda (var)
(set var 0))
'(a b c))
which could assign a value (here it is '0') to variables from list (here they are 'a', 'b' and 'c').
No. And to see why there isn't consider something like this:
(define (mutant var val)
(let ((x 1))
(set var val)
x))
Now, what should (mutant 'x 3) return? If it should return 3, then:
set can't be a function since it needs access to the lexical environment of mutant;
any kind of reasonable compilation of this function is not possible.
If you want set to be a function then catastrophe follows. Consider this definition:
(define (mutant-horror f)
(let ([x 3])
(f)
x))
Now, you would think that this can be optimised to this:
(define (mutant-horror f)
(f)
3)
But it can't. Because you might call it like this:
(mutant-horror (λ () (set 'x 3)))
or, more generally, you might call it with a function which, somewhere eventually in some function called indirectly from it might end up saying (set 'x 3).
This means that no binding can ever be optimised at all, which is a disaster. It's also at least very close to meaning lexical scope is not possible: if as well as set, a function called get exists, which retrieves the binding of a symbol, then you have, essentially, dynamic scope. That in turn makes things like tail-call elimination at least difficult and probably impossible (in fact set probably does this on its own).
Reasons like this are why even very old Lisps, where things like set did exist and did superficially work, actually made special exemptions for compiled code, where set didn't work (see for instance the Lisp 1.5 programmer's manual (PDF link), appendix D. This divergence between the semantics of compiled and interpreted code is one of the things that later Lisps and Lisp-related languages such as CL and Scheme did away with.
If instead you want something like Common Lisp's semantics, where the equivalent thing
(defun mutant (var val)
(let ((x 1))
(set var val)
x))
Would return 1 (unless x was a globally (see below) special variable, in which case it might return something else) and as a side-effect modify the value cell of whatever symbol was named by var (which might be x), then, well, Scheme has no notion of that at all, and that's a good thing on the whole.
Note that a modified version of the function will also work for locally special variables:
(defun mutant/local-special (a b)
(let ((x 1))
(declare (special x))
(set a b)
x))
But in this case you always know there's a special binding happening because you can always see the declaration.
When you write something like
(map (lambda (var)
(set var 0))
'(a b c))
my first thought was that you try to accumulate unordered sets of the form ( (a 0) (b 0) (c 0) ).
You cannot implement your own setter for any of the internal data structures that are provided by the language as this would mean to write a scheme function to modify some data structures that is implemented in C. For the data structures implemented in C you need to provide setters written in C -- supposing the lower language is C.
If you want to implement your own setter you either
-- check how the data structure in implmented, and if it's implemented in scheme you will undestand how to modify it
-- define your own data structure using already existing data structures and define setters for it.
A setter that mutates the data structure contains a ! at the end of its name, such as set!, append!, etc.
A function call just evaluates a series of instructions in an extended environment, the environment in particular is that of definition extended with its function parameters, this is prety much the case in any language...
If you do this:
(define (my-set var val)
(set! var val))
you will bind the value of val to var, but only within the scope of the current call on my-set. The reason you cannot write such function has to do with the nature of scheme itself, var is a pointer to whatever you pass in the function, but set! will make this pointer point to something else (still within the scope of my-set). my-set could work if we had some sort of mechanism of using actual pointers, as some low lever languages allow. But scheme does not...
Note that scheme goes very well with the functional programming style as well as recursion, so if you have a need for a function as you described, you are probably "doing something wrong"... :)
You can, however, do this:
(define my-list (list 1 2 3))
(define (my-set a-list a-value)
(set-car! my-list a-value))
> (my-set my-list 4)
> my-list
(4 2 3)
this works since a-list is a pointer to a cons-cell, set-car! will modify the contents of a cons-cell, but not affect the pointer to which.

Sequence Creation With Abstract/Concrete Syntax (Scheme)

To start off, this is homework. It isn't my intention to get a complete solution, but I am stuck with how to interpret this problem.
We're basically making an interpreter from a lexer and parser for a language that we have created in class.
Specifically, what I am stuck on is using Scheme/Drracket to implement creating a sequence using the parser that calls the method "make-seq" that can be defined by the following rules:
(make-seq (e)) = e
(make-seq (e1 e2 ... en)) = (make-let '*temp* e1 (make-seq (e2 ... en)))
Where make-let is defined as:
(define (make-let var exp1 exp2)
(list 'let var exp1 exp2))
I've implemented the make-seq method below:
(define (make-seq e)
(if (null? (cdr e))
e
(make-let '*temp* (car e) (make-seq (cdr e)))))
but I am not sure how to represent the 'temp in the rules above. I am fairly certain that I should not have the symbol 'temp actually in the implementation, but I'm not sure what I should be doing there instead.
Could anyone give me pointers as to how I should move forward with this?
Thanks!
Using *temp won't be a problem unless the program it's interpreting also has *test:
(let ((*temp 10))
(seq
expression1
(* *temp *temp))) ; end up using the result from the previous element in the sequence
To fix this you can introduce variables that are ok for the interpreter but not possible to create in code. I've often used symbols and lists where the first element is a special value. Your make-seq creates that instead and your interpreter needs to treat those as symbols too. Usually the interpreter uses eq? so basically every structure created as a list with a tag will be unique for that expansion making each nesting *temp not even bother the other ones created earlier.

Cond statements in Scheme/Racket

I am quite confused with cond statements -
I am wondering how I would write something along the lines of this using just the one cond:
(define (name x)
(cond [(statement 1 is true)
[(if statement 1 and statement 2 are both true) *print*]
[(if statement 1 is true and statement 2 is not) *print*]])
[else (statement 1 is false)
[(if statement 1 is false and statement 2 is true) *print*]
[(if statement 1 and statement 2 are both false) *print*]])
(pseudo conditions)
Thank you
Since you are mentioning statement-1 and statement-2 I think your code can be heavily optimized since nested if has already determined the state of statement-1. here is how I see it:
(if statement-1
(if statement-2 ; statement-1 consequent
consequent-1-2-true
alternative-1-true-2-false)
(if statement-2 ; statement-1-alternative
consequent-1-false-2-true
alternative-1-2-false))
statement-2 will only be evaluated once since statement-1 will either be false or true.
A cond is more useful when you have a simple if-elseif where only the alternative has nested ifs.
(cond (predicate-1 consequent-1)
(predicate-2 consequent-2)
...
(predicate-n consequent-2)
(else alternative))
Now you could use a let to calculate the statements in advanced and use and to check that more than one are true:
(let ((result-1 statement-1) (result-2 statement-2))
(cond ((and result-1 result-2) consequent-1-2-true)
(result-1 consequent-1-true-2-false)
(result-2 consequent-1-false-2-true)
(else alternative-1-2-false)))
Of course this let is not necessary if the statements are variables themselves and thus cached. Notice that the tests after the first don't need to check that one is true and the second is not since we know that both cannot be true, then the previous consequent would have fired and the cond had been done. All the perdicates can assume all previous predicates were false.
When writing code it's better to think about what makes the code easier to read and understand. In this case I would not have used cond since the nature of your problem has even nesting on both the consequent and alternative.
Just a followup to my second suggestion (not an answer):
(define (name x)
(if 'statement 1 is true'
(if 'statement 2 is true'
*print1*
*print2*)
(if 'statement 2 is true'
*print3*
*print4*)))
This covers the boolean cases.

case and quotation in scheme & racket

I was a bit surprised by this racket code printing nay when I expected yeah:
(define five 5)
(case 5
[(five) "yeah"]
[else "nay"])
Looking at the racket documentation for case makes it clearer:
The selected clause is the first one with a datum whose quoted form is equal? to the result of val-expr.
So it's about quotation. I'm pretty sure that I did not yet fully grasp what quotation in lisps can buy me. I understand it in the viewpoint of macros and AST transformation. However I'm confused why is it helpful in the case of case for instance..?
I'm also curious, with this specification of case, can I use it to achieve what I wanted to (compare the actual values, not the quoted value), or should I use another construct for that? (cond, while strictly more powerful, is more verbose for simple cases, since you must repeat the predicate at each condition).
The problem is that case introduces implicit quote forms, which cause your example to work for 'five (whose value is 'five), instead of five (whose value is 5).
I almost never use case because of exactly this problem. Instead I use racket's match form with the == pattern:
(define five 5)
(define (f x)
(match x
[(== five) "yeah"]
[_ "nay"]))
(f 5) ; "yeah"
(f 6) ; "nay"
This produces "yeah" on only the value 5, just like you expected. If you wanted it to return "yeah" when it's equal to either five or six, you can use an or pattern:
(define five 5)
(define six 6)
(define (f x)
(match x
[(or (== five) (== six)) "yeah"]
[_ "nay"]))
(f 5) ; "yeah"
(f 6) ; "yeah"
(f 7) ; "nay"
And if you really want to match against quoted datums, you can do that by writing an explicit quote form.
(define (f x)
(match x
[(or 'five 'six) "yeah"]
[_ "nay"]))
(f 5) ; "nay"
(f 6) ; "nay"
(f 7) ; "nay"
(f 'five) ; "yeah"
(f 'six) ; "yeah"
These quote forms are implicit and invisible when you use case, lurking there waiting to cause confusion.
The Racket documentation gives this grammar:
(case val-expr case-clause ...)
where
case-clause = [(datum ...) then-body ...+]
| [else then-body ...+]
Let's compare to your example:
(define five 5)
(case 5 ; (case val-expr
[(five) "yeah"] ; [(datum) then-body1]
[else "nay"]) ; [else then-body2])
We see that (five) is interpreted as (datum). This means that five is
a piece of data (here a symbol), not an expression (later to be evaluated).
Your example of case is evaluated like this:
First the expression 5 is evaluated. The result is the value 5.
Now we look at a clause at a time. The first clause is [(five) "yeah"].
Is the value 5 equal (in the sense of equal?) to one of the datums in (five)? No, so we look at the next clause: [else "nay"]. It is an else-clause so the expression "nay" is evaluated and the result is the value "nay".
The result of the case-expression is thus the value "nay".
Note 1: The left-hand sides of case-clauses are datums (think: they are implicitly quoted).
Note 2: The result of val-expr is compared to the clause datums using equal?. (This is in contrast to Scheme, which uses eqv?.
UPDATE
Why include case? Let's see how one can write the example using cond:
(define five 5)
(let ([val five])
(cond
[(member val '(five)) "yeah"]
[(member val '(six seven)) "yeah"] ; added
[else "nay"])
This shows that one could do without case and just use cond.
However - which version is easier to read?
For a case expression it is easy to see which datums the value is compared to.
Here one must look closely to find the datums. Also in the example we know beforehand that we are trying to find the value among a few list of datums. In general we need to examine a cond-expression more closely to see that's what's happening.
In short: having a case-expression increases readability of your code.
For the historically interested: https://groups.csail.mit.edu/mac/ftpdir/scheme-mail/HTML/rrrs-1986/msg00080.html disussed whether to use eqv? or equal? for case.
UPDATE 2
I'll attempt to given an answer to:
I'm still not clear on the quotation vs working simply on the values though.
I'm wondering specifically why doing the quotation, why working on datum instead
of working on values. Didn't get that bit yet.
Both approaches make sense.
Let's for the sake of argument look at the case where case uses expressions rather than datums in the left hand side of a clause. Also following the Scheme tradition, let's assume eqv? is used for the comparison. Let's call such a
case-expression for ecase (short for expression-case).
The grammar becomes:
(ecase val-expr ecase-clause ...)
where
ecase-clause = [(expr ...) then-body ...+]
| [else then-body ...+]
Your example now becomes:
(define five 5)
(ecase five
[('five) "yeah"]
[else "nay")
This doesn't look too bad and the result is what we are used to.
However consider this example:
(ecase '(3 4)
[('five (list 3 4) "yeah"]
[else "nay")
The result of this would be "nay". The two lists resulting from evaluating the expressions '(3 4) and (list 3 4) are not equal in the sense of eqv?.
This shows that if one chooses to use eqv? for comparisions, having expressions available on the left hand side won't be helpful. The only values that work with eqv? atomic values - and therefore one could just as well use implicit quotations and restrict the left hand side to datums.
Now if equal? was used it would make much more sense to use expressions on the left hand side. The original Racket version of case was the same as the one in Scheme (i.e. it used eq?) later on it was changed to used equal?. If case was designed from scratch, I think, expressions would be allowed rather than datums.
The only remaining issue: Why did the authors of Scheme choose eqv? over equal? for comparisons? My intuition is that the reason were performance (which back in the day was more important than now). The linked to post from the rrrs-authors mailing list gives two options. If you dig a little further you might be able to find responses.
I can't find a reference right now, but case statements use literal, unevaluated data in their different clauses because it is both a frequent use-case and more easily subject to efficient compilation.
You could probably write your own version of Clojure's condp macro or a custom conditional operator to handle your use case.

How is 'and' defined in Scheme?

'and' in Scheme will ignore the error 'division by 0', like (and (negative? (random 100)) (/ 1 0)) returns #f.
How does it do that?
i (define (∧ b₀ b₁) (if (not b₀) #f b₁)), (∧ (negative? (random 100)) (/ 1 0)) still goes into a 'division by 0' error.
You can't define and directly as a function because Scheme is strict--this means that function arguments are always evaluated before being passed to the function.
However, you can define a proper short-circuiting and using a macro. Here's the simplest version in Racket:
(define-syntax-rule (my-and a b) (if a b #f))
or the equivalent form using syntax-rules which is in standard Scheme:
(define-syntax my-and
(syntax-rules ()
[(_ a b) (if a b #f)]))
A macro is not a normal function. Instead, it's a syntactic transformation that runs at compile-time. When you use your new and in your code, it gets "expanded" to the corresponding if expression. So:
(my-and #f (/ 1 0))
gets transformed into
(if #f (/ 1 0) #f)
before your program is run. Since if is built into the language, this has the correct behavior.
Since macros are not functions, it also means you can't pass around as arguments. So you can't write a fold using and directly--you'd have to wrap it into a lambda.
To be more faithful to the original and, you could define my-and to take an arbitrary number of arguments by making the macro recursive. To define a "rest parameter" in a macro, you use the special ... keyword:
(define-syntax my-and
(syntax-rules ()
[(_) #t]
[(_ a) a]
[(_ a b ...) (if a (my-and b ...) #f)]))
If you were using a lazy language like Racket's #lang lazy or Haskell instead, you would not need to use macros here. You could just define and directly:
#lang lazy
(define (and a b) (if a b #f))
or in Haskell:
and a b = if a then b else False
and it would have the correct behavior, as a normal function. You would be able to pass this and to a fold, and it would even stop evaluating the list as soon as it encountered a False! Take a look:
Prelude> foldl and True [True, False, error "fail"]
False
(error in Haskell errors out just like 1/0. Since Haskell is statically typed, the arguments to and have to be booleans so you can't use 1/0 directly.)
Like most languages, Scheme's logical AND uses short circuit evaluation, which means its right operand will only be evaluated if the left operand is true. If the left operand is false, then the result of the expression must be false regardless of the value of the right operand, so if the left operand evaluates to false, it returns false immediately, without evaluating the right operand at all.
To be precise, here's the language from the spec (I'm section 4.2.1 of R5RS, but this isn't an area that's likely to change must between revisions of the spec):
(and <test1> ... )
The <test> expressions are evaluated from left to right, and the value of the first expression that evaluates to a false value (see section 6.3.1) is returned. Any remaining expressions are not evaluated.
Boolean shortcutting. The first argument to "and" evaluates to false, therefore the result must be false, and there's no reason for it to evaluate the second argument (and therefore incur the error).

Resources