Scheme: problem about display when using `delay` expression - stream

This is a problem related to ex3.51 in SICP, here is the code
(define (cons-stream x y)
(cons x (delay y)))
(define (stream-car stream) (car stream))
(define (stream-cdr stream) (force (cdr stream)))
(define (stream-map proc s)
(if (stream-null? s)
the-empty-stream
(cons-stream
(proc (stream-car s))
(stream-map proc (stream-cdr s)))))
(define (stream-enumerate-interval low high)
(if (> low high)
the-empty-stream
(cons-stream
low
(stream-enumerate-interval (+ low 1) high))))
(define (stream-ref s n)
(if (= n 0)
(stream-car s)
(stream-ref (stream-cdr s) (- n 1))))
(define (show x)
(display x)
x)
;test
(stream-map show (stream-enumerate-interval 0 10))
the output is 012345678910(0 . #<promise>).
but I thought the delay expression in cons-stream delayed the evaluation, if i use a different processing function in stream-map like lambda (x) (+ x 1) the output (1 . #<promise>) is more reasonable, so why does display print all the numbers?

The problem is with this definition:
(define (cons-stream x y)
(cons x (delay y)))
It defines cons-stream as a function, since it uses define.
Scheme's evaluation is eager: the arguments are evaluated before the function body is entered. Thus y is already fully calculated when it is passed to delay.
Instead, cons-stream should be defined as a macro, like
(define-syntax cons-stream
(syntax-rules ()
((_ a b) (cons a (delay b)))))
or we can call delay explicitly, manually, like e.g.
(define (stream-map proc s)
(if (stream-null? s)
the-empty-stream
(cons
(proc (stream-car s))
(delay
(stream-map proc (stream-cdr s))))))
Then there'd be no calls to cons-stream in our code, only the (cons A (delay B)) calls. And delay is a macro (or special form, whatever), it does not evaluate its arguments before working but rather goes straight to manipulating the argument expressions instead.
And we could even drop the calls to delay, and replace (cons A (delay B)) with (cons A (lambda () B)). This would entail also reimplementing force (which is built-in, and goes together with the built-in delay) as simply (define (force x) (x)) or just calling the (x) manually where appropriate to force a stream's tail.
You can see such lambda-based streams code towards the end of this answer, or an ideone entry (for this RosettaCode entry) without any macros using the explicit lambdas instead. This approach can change the performance of the code though, as delay is memoizing but lambda-based streams are not. The difference will be seen if we ever try to access a stream's element more than once.
See also this answer for yet another take on streams implementation, surgically modifying list's last cons cell as a memoizing force.

Related

Delayed "let" in SICP

SICP indicates cdr is opened up:
In section 3.5.4 , i saw this block:
(define (integral delayed-integrand initial-value dt)
(define int
(cons-stream initial-value
(let ((integrand (force delayed-integrand)))
(add-streams (scale-stream integrand dt)
int))))
int)
Normally if this was something like:
(define (stream-map proc s)
(if (stream-null? s)
the-empty-stream
(cons-stream (proc (stream-car s))
(stream-map proc (stream-cdr s)))))
The stream-cdr s would be evaluated as (cons-stream (stream-car (cdr s)) delay<>) even when the actual call would be in a delay. ie even though the stream-map function itself is delayed, the arguments are pre-computed. [Is this correct? - By the applicative model, the arguments should be substituted for ,before the function is "called", but is the call evaluation when delay is forced or when it's just specified]
Then why is let not pre-computed?
What i think? I think let is a lambda function with the variable as the arguments, so it's execution is delayed
(let ((var1 e1) (var2 e2)) e3)
is same as
Lambda (var1 var2) e3 (with var1 bound to e1 and var2 bound to e2)
Can someone please help me confirm this? Thanks
In SICP type streams the car of a stream is not delayed, but the cdr is.
The whole expression,
(let ((integrand (force delayed-integrand)))
(add-streams (scale-stream integrand dt)
int))
, is delayed since it is the second argument to cons-stream. What kind of expression is delayed doesn't matter so you can have a call, evaluation of variable or even a let there.
"the arguments are pre-computed. is this correct?"
No. (cons-stream a (func (stream-cdr b))) is just like
(cons-stream a
(lambda ()
;; our code is placed here, verbatim, as a whole:
(func (stream-cdr b))
)
)
const-stream is a macro, it just moves pieces of code around.
The lambda might be enclosed in a memo-proc call for the memoization, to do call-by-need, but it'll still have the code we wrote, like (func (stream-cdr b)), placed inside the lambda, "textually", by cons-stream. Which is a macro, just moving pieces of code around.
Regarding the snippet which you've added to the question, the authors were just being imprecise. What is meant there is, when (stream-cdr stream) in (stream-filter pred (stream-cdr stream)) will be called, it will produce (cons 10008 (delay .... )), as shown. Not before.
Where it says "which in this case is" is should have said "which in this case is the same as".
In section 3.5.1 Streams Are Delayed Lists the book says:
To make the stream implementation automatically and transparently interleave the construction of a stream with its use, we will arrange for the cdr of a stream to be evaluated when it is accessed by the stream-cdr procedure rather than when the stream is constructed by cons-stream.

Racket: Creating a stream using a macro instead of a function

I'm currently attempting to create a stream using a macro, as shown below:
(define-syntax create-stream
(syntax-rules (using starting at with increment )
[(create-stream name using f starting at i0 with increment delta)
(letrec
([name (lambda (f current delta)
(cons current (lambda () (name (f (+ current delta) delta)))))])
(lambda () (name f i0 delta)))
]))
What happens though is I get a compilation error later on when I try to pass it the following lambda function, which says "lambda: not an identifier, identifier with default, or keyword".
(create-stream squares using (lambda (x) (* x x)) starting at 5 with increment 2)
What I suspect is happening is that by attempting to use lambda in a macro, it's shadowing the actual lambda function in Racket. If this is true, I'm wondering in what way one can create a stream without using lambda, since as far as I can tell, there needs to be a function somewhere to create said stream.
Also this is a homework problem, so I do have to use a macro. My equivalent function for creating the stream would be:
(define create-stream
(letrec ([name (lambda (f current delta) (cons current (lambda () (name (f (+ current delta) delta)))))])
(lambda () (name f i0 delta))))
What I suspect is happening is that by attempting to use lambda in a macro, it's shadowing the actual lambda function in Racket
No, that's not correct.
The problem here is that you use f and delta in binding positions, in (lambda (f current delta) ...). That means after (create-stream squares using (lambda (x) (* x x)) starting at 5 with increment 2) is expanded, you will get code like:
(lambda ((lambda (x) (* x x)) current 2) ...)
which is obviously a syntax error.
You can see this yourself by using the macro stepper in DrRacket.
The fix is to rename the identifiers to something else. E.g.,
(define-syntax create-stream
(syntax-rules (using starting at with increment )
[(create-stream name using f starting at i0 with increment delta)
(letrec
([name (lambda (f* current delta*)
(cons current (lambda () (name (f* (+ current delta*) delta*)))))])
(lambda () (name f i0 delta)))
]))
This will make your code start running, but there are still other several bugs in your code. Since this is your homework, I'll leave them to you.
Your current problem essentially boils down to this (broken) code
(let ((x 0))
(define y x))
This is not equivalent to (define y 0) - y's definition is local to the let-binding.
The solution is to flip the definitions around and make the letrec local to the define:
(define name
(letrec (... fn definition ...)
(lambda () (fn i0))))

SICP Infinite Streams (Chapter 3.5.2)

This is a question related to the SICP Book Chapter 3.5.2.
I'm implementing a stream data structure in other programming languages. And I'm not sure if I understand the following snippet correctly.
(define (integers-starting-from n)
(cons-stream n (integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
From what I understood at (integers-starting-from (+ n 1)) will execute the function which return a value by executing(cons-stream n (integers-starting-from (+ n 1)))). Because the second formal parameter of the cons-stream is (integers-starting-from (+ n 1)), and because it is enclosed by( ), thus it will execute the function again and again infinitely instead of delaying the execution.
From what I see before executing this snippet, It seems that the following integer will leads to an infinite recursive before even the seconds element of the stream being executed.
Why does this seems to work for scheme as shown during the lecture?
From my understand it should be written something like this instead:
(define (integers-starting-from n)
(cons-stream n (lambda() (integers-starting-from (+ n 1)))))
(define integers (integers-starting-from 1))
Does this means that scheme has some kinds of magic that delay the execution of (integers-starting-from (+ n 1)) ?
Thank you in advance
The trick lies in how we implement cons-stream. You explicitly created an evaluation promise when you defined the (lambda () ...) thunk. The special form cons-stream does this, but implicitly and using Scheme's primitives. For example, it can be implemented like this, notice how we use delay:
(define-syntax stream-cons
(syntax-rules ()
((stream-cons head tail)
(cons head (delay tail)))))
It makes more sense to encapsulate all the promise-creation logic in a single place, say cons-stream, instead of explicitly creating thunks everywhere.

Why use this style of lambda in Racket?

I am brushing up on my low Racket knowledge and came across this streams in racket excellent post. My question is about this snip:
(define powers-of-two
(letrec ([f (lambda (x) (cons x (lambda () (f (* x 2)))))])
(lambda () (f 2))))
I understand the reason for the 'inner lambdas' but why did the OP use a lambda for the whole function? Couldn't it be done like this just as effectively?
(define (powers-of-two)
(letrec ([f (lambda (x) (cons x (lambda () (f (* x 2)))))])
(f 2)))
I experimented and see no difference. My question is whether this is just a matter of style, or if there is some reason that the former is preferable.
There is no difference. Since f in the second example doesn't close over anything, it can be lifted outside the powers-of-two function, which is equivalent to the first example.
One reason why the first might be preferable is that there is only ever one f function that needs to be created. With the second one, a new f function would be created every time someone called (powers-of-two).
I tried timing them both though, and neither was significantly faster than the other.
(define (name arg)
arg)
This is the short form of writing:
(define name
(lambda (arg)
arg))
So what happens with the first example is that the letrect happens right away and the function returned would be (lambda () (f 2)) with f in it's closure.
The second makes a procedure called powers-of-two that , when applied (powers-of-two) will return the same as the first powers-of-two is.. Think of it as a powers-of-two-generator.
Thus:
(define powers-of-two
(letrec ([f (lambda (x) (cons x (lambda () (f (* x 2)))))])
(lambda () (f 2))))
(define (powers-of-two-generator)
(letrec ([f (lambda (x) (cons x (lambda () (f (* x 2)))))])
(f 2)))
(powers-of-two) ; ==> (2 . procedure)
(define powers-of-two2 (powers-of-two-generator)) ; ==> procedure
(powers-of-two2) ; ==> (2 . procedure)
Do you see the difference?

Scheme streams with Taylor series

I've been doing some homework, wrote some code and can't actually find the reason why it doesn't work. The main idea of this part of the work is to make a stream that will give me elements of Taylor series of cosine function for a given X (angle i guess). anyways here is my code, I'd be happy if some one could point me to the reasons it doesn't work :)
(define (force exp) exp)
(define (s-car s) (car s))
(define (s-cdr s) (force (cdr s)))
; returns n elements of stream s as a list
(define (stream->list s n)
(if (= n 0)
'()
(cons (s-car s) (stream->list (s-cdr s) (- n 1)))))
; returns the n-th element of stream s
(define stream-ref (lambda (s n)
(if (= n 1)
(s-car s)
(stream-ref (s-cdr s) (- n 1)))))
; well, the name kinda gives it away :) make factorial n!
(define (factorial x)
(cond ((= x 0) 1)
((= x 1) 1)
(else (* x (factorial (- x 1))))))
; this function is actually the equation for the
; n-th element of Taylor series of cosine
(define (tylorElementCosine x)
(lambda (n)
(* (/ (expt -1 n) (factorial (* 2 n))) (expt x (* 2 n)))))
; here i try to make a stream of those Taylor series elements of cosine
(define (cosineStream x)
(define (iter n)
(cons ((tylorElementCosine x) n)
(lambda() ((tylorElementCosine x) (+ n 1)))))
(iter 0))
; this definition should bind cosine
; to the stream of taylor series for cosine 10
(define cosine (cosineStream 10))
(stream->list cosine 10)
; this should printi on screen the list of first 10 elements of the series
However, this doesn't work, and I don't know why.
I'm using Dr.Scheme 4.2.5 with the language set to "Essentials of Programming Languages 3rd ed".
Since I was feeling nice (and nostalgic about scheme) I actually waded through your code to finde the mistakes. From what I can see there are 2 problems which keeps the code from running as it should:
If I understand your code correctly (force exp) should evaluate exp, however you directly return it (unevaluated). So it probably should be defined as (define (force exp) (exp))
The second problem is in your lambda: (lambda() ((tylorElementCosine x) (+ n 1)) ) will evaluate to the next element of the taylor series, while it should evaluate to a stream. You probably want something like this: (lambda() (iter (+ n 1)) )
I haven't checked if the output is correct, but with those modifications it does at least run. So if there are any more problems with the code the should be in the formula used.
However I'd suggest that next time you want help with your homework you at least tell us where exactly the problem manifests and what you tried already (the community does frown on "here is some code, please fix it for me" kind of questions).

Resources