Exercise 1.5. Ben Bitdiddle has invented a test to determine whether the interpreter he is faced with is using applicative-order
evaluation or normal-order evaluation. He defines the following two
procedures:
(define (p) (p))
(define (test x y) (if (= x 0)
0
y))
Then he evaluates the expression
(test 0 (p))
What behavior will Ben observe with an interpreter that uses
applicative-order evaluation? What behavior will he observe with an
interpreter that uses normal-order evaluation?
I understand the answer to the exercise; my question lies in how (p) is interpreted versus p. For example, (test 0 (p)) causes the interpreter to hang (which is expected), but (test 0 p) with the above definition immediately evaluates to 0. Why?
Moreover, suppose we changed the definition to (define (p) p). With the given definition, (test 0 (p)) and (test 0 p) both evaluate to 0. Why does this occur? Why doesn't the interpreter hang? I am using Dr. Racket with the SICP package.
p is a function. (p) is a call to a function.
In your interpreter evaluate p.
p <Return>
==> P : #function
Now evaluate (p). Make sure you know how to kill your interpreter! (Probably there is a “Stop” button in Dr. Racket.)
(p)
Note that nothing happens. Or, at least, nothing visible. The interpreter is spinning away, eliminating tail calls (so, using near 0 memory), calling p.
As p and (p) evaluate to different things, you should expect different behaviour.
As to your second question : You are defining p to be a function that returns itself. Again, try evaluating p and (p) with your (define (p) p) and see what you get. My guess (I am using a computer on which I cannot install anything and which has no scheme) is that they will evaluate to the same thing. (I might even bet that (eq? p (p)) will evaluate to #t.)
Related
In Racket (and other Schemes, from what I can tell), the only way I know of to check whether two things are not equal is to explicitly apply not to the test:
(not (= num1 num2))
(not (equal? string1 string2))
It's obviously (not (that-big-of-deal?)), but it's such a common construction that I feel like I must be overlooking a reason why it's not built in.
One possible reason, I suppose, is that you can frequently get rid of the not by using unless instead of when, or by switching the order of the true/false branches in an if statement. But sometimes that just doesn't mimic the reasoning that you're trying to convey.
Also, I know the negated functions are easy to define, but so is <=, for example, and that is built in.
What are the design decisions for not having things like not-equal?, not-eqv?, not-eq? and != in the standard library?
First, you are correct that it is (not (that-big-of-a-deal?))1
The reason Racket doesn't include it out of the box is likely just because it adds a lot of extra primitives without much benefit. I will admit that a lot of languages do have != for not equal, but even in Java, if you want to do a deep equality check using equals() (analogous to equal? in Racket), you have to manually invert the result with a ! yourself.
Having both <= and > (as well as >= and <) was almost certainly just convenient enough to cause the original designers of the language to include it.
So no, there isn't any deep reason why there is not any shortcut for having a not-eq? function built into Racket. It just adds more primitives and doesn't happen to add much benefit. Especially as you still need not to exist on its own anyway.
1I love that pun by the way. Have some imaginary internet points.
I do miss not having a not= procedure (or ≠ as mentioned in #soegaard's comment), but not for the reasons you think.
All the numeric comparison operators are variadic. For example, (< a b c d) is the same as (and (< a b) (< b c) (< c d)). In the case of =, it checks whether all arguments are numerically equal. But there is no procedure to check whether all arguments are all unequal—and that is a different question from whether not all arguments are equal (which is what (not (= a b c d)) checks).
Yes, you can simulate that procedure using a fold. But still, meh.
Edit: Actually, I just answered my own question in this regard: the reason for the lack of a variadic ≠ procedure is that you can't just implement it using n-1 pairwise comparisons, unlike all the other numeric comparison operators. The straightforward approach of doing n-1 pairwise comparisons would mean that (≠ 1 2 1 2) would return true, and that's not really helpful.
I'll leave my original musings in place for context, and for others who wonder similar things.
Almost all of the predicates are inherited by Scheme, the standard #!racket originally followed. They kept the number of procedures to a minimum as a design principle and left it to the user to make more complex structures and code. Feel free to make the ones you'd like:
(define not-equal? (compose1 not equal?))
(define != (compose1 not =))
; and so on
You can put it in a module and require it. Keep it by convention so that people who read you code knows after a minute that everything not-<known predicate> and !-<known-predicate> are (compose not <known-predicate>)
If you want less work and you are not after using the result in filter then making a special if-not might suffice:
(define-syntax-rule (if-not p c a) (if p a c))
(define (leafs tree)
(let aux ((tree tree) (acc 0))
(if-not (pair? tree)
(+ acc 1) ; base case first
(aux (cdr tree)
(aux (car tree) acc)))))
But it's micro optimizations compared to what I would have written:
(define (leafs tree)
(let aux ((tree tree) (acc 0))
(if (not (pair? tree))
(+ acc 1) ; base case first
(aux (cdr tree)
(aux (car tree) acc)))))
To be honest if I were trying to squeeze out a not I would just have switched them manually since then optimizing speed thrumps optimal readability.
I find that one easily can define != (for numbers) using following macro:
(define-syntax-rule (!= a b)
(not(= a b)))
(define x 25)
(!= x 25)
(!= x 26)
Output:
#f
#t
That may be the reason why it is not defined in the language; it can easily be created, if needed.
I am converting some Scheme code to Common Lisp. I don't know Scheme. I know a bit of Common Lisp.
Here is the Scheme code:
(define (with-process-abortion thunk)
(call-with-current-continuation
(lambda (k)
(fluid-let ((*abort-process* k))
(thunk)))))
I did some reading on the Scheme call-with-current-continuation function but, honestly, I have no idea what the above function is doing. My conversion to Common Lisp is very skeletal at this time:
(defun with-process-abortion (thunk)
;; No idea how to implement
)
This SO post says:
every occurrence of call/cc can be replaced with the following
equivalent:
(lambda (f k) (f (lambda (v k0) (k v)) k))
where k is the continuation to be saved, and (lambda (v k0) (k v)) is
the escape procedure that restores this continuation (whatever
continuation k0 that is active when it is called, is discarded).
Okay, what would f correspond to in my situation? What would k correspond to?
You cannot solve this problem in general because Common Lisp does not have call/cc or anything that implements "full continuations." However, you can probably convert this code because it appears that the Scheme implementation is using call/cc only for non-local exits, and Common Lisp supports this with catch and throw, as well as with restarts.
You could try replacing a uses of (with-process-abortion thunk) with
`(catch 'wpa #,thunk)
and replace (*abort-process*) with (throw 'wpa nil)
I can't seem to come up with an example of this and wondering if there is such a case? I know if I have an expression where applicative order doesn't terminate that normal order may still terminate. I'm wondering though if there is an example where both orders terminate but normal order has fewer steps.
(λ p. λ q. q) ((λ x. λ y. λ z. ((x y) z)) (λ w. λ v. w))
With some whitespace:
(λ p.
λ q.
q
)
(
(λ x.
λ y.
λ z.
((x y) z)
)
(λ w.
λ v.
w
)
)
In normal order, the outermost reduction can be performed first, reducing directly to the identity combinator in one step. Applicative order will get there too, but it takes much longer since the x-y-z-w-v expression needs to be evaluated first.
Note that the x-y-z-w-v expression isn't even used. You can think of normal order as a sort of lazy evaluation: expressions are only evaluated or reduced when they are used. So you just build a formula that doesn't use one of its arguments and you immediately have an example of this kind of efficiency win.
In a lambda expression, any variable bound within an abstraction can be used zero or more times in the body of the abstraction.
Normal order evaluates the argument n times, where n is the number of times it is used in the body.
Applicative order evaluates the argument exactly once, irrespective of the number of times it is used in the body.
Comparison
If argument is used exactly once, both normal order and applicative order will have same performance.
If the argument is used more than once, applicative order will be faster.
If the argument is used zero times, normal order would be faster.
I've read and somewhat understand Use of lambda for cons/car/cdr definition in SICP. My problem is understanding the why behind it. My first problem was staring and staring at
(define (cons x y)
(lambda (m) (m x y)))
and not understanding how this function actually did any sort of consing. Consing as I learned it from various Lisp/Scheme books is putting stuff in lists, i.e.,
(cons 1 ()) => (1)
how does
(define (cons x y)
(lambda (m) (m x y)))
do anything like consing? But as the light went on in my head: cons was only sort of a placeholder for the eventual definitions of car and cdr. So car is
(define (car z)
(z (lambda (p q) p)))
and it anticipates an incoming z. But what is this z? When I saw this use:
(car (cons 1 2))
it finally dawned on me that, yes, the cons function in its entirety is z, i.e., we're passing cons to car! How weird!
((lambda (m) (m 1 2)) (lambda (p q) p)) ; and then
((lambda (p q) p) 1 2)
which results in grabbing the first expression since the basic car operation can be thought of as an if statement where the boolean is true, thus, grab the first one.
Yes, all lists can be thought of as cons-ed together expressions, but what have we won by this strangely backward definition? It's as if any initial, stand-alone definition of cons is not germane. It's as if uses of something define that something, as if there's no something until its uses circumscribe it. Is this the primary use of closures? Can someone give me some other examples?
but what have we won by this strangely backward definition?
The point of the exercise is to demonstrate that data structures can be defined completely in terms of functions; that data structures are not necessary as a primitive construct in a language -- if you have functions (that are closures), that's sufficient. This shows the power of functions, and is probably mind-boggling to someone from outside of functional programming.
It's not that in a real project we would actually define data structures this way. It would be more efficient to use language-provided data structure constructs. But it's important to know that we can do it this way. In computer science, it's useful to be able to "reduce" one construct (data structures) into another construct (functions) so that if we prove something about the second construct, it applies to the first one too.
I've read something about tail-call optimization in Scheme. But I'm not sure whether I understand the concept of tail calls. If I have code like this:
(define (fac n)
(if (= n 0)
1
(* n (fac (- n 1)))))
can this be optimized, so that it won't take stack memory?
or can tail-call optimization only be applied to a function like this:
(define (factorial n)
(let fact ([i n] [acc 1])
(if (zero? i)
acc
(fact (- i 1) (* acc i)))))
A useful way to think about tail calls is to ask "what must happen to the result of the recursive procedure call?"
The first function cannot be tail-optimised, because the result of the internal call to fac must be used, and multiplied by n to produce the result of the overall call to fac.
In the second case, however, the result of the 'outer' call to fact is... the result of the inner call to fact. Nothing else has to be done to it, and the latter value can simply be passed back directly as the value of the function. That means that no other function context has to be retained, so it can simply be discarded.
The R5RS standard defines 'tail call' by using the notion of a tail context, which is essentially what I've described above.
No, the first fac cannot be optimized.
When a function is called, you need to know the place it was called from, so that you can return to that location once the call is complete and use the result of the call in future computations (a fac function).
fact is implemented differently. The last thing that fact does is to call itself. There is no need to remember the place we are calling from — instead, we can perform tail call elimination. There is no other actions which should be done after the fact returns.