问题
Structure and Interpretation of Computer Programs (SICP) 3.5.2 introduces infinite streams:
(define ones
(cons-stream 1 ones))
This code doesn't work in DrRacket, with the error:
ones: undefined; cannot reference an identifier before its definition
Other code like this:
(define (integers-starting-from n)
(cons-stream n
(integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
produce error:
Interactions disabled
(fall in infinite loop?)
So far as I read(SICP), the key point of implementing an infinite stream is delayed evaluation:
(define (delay exp)
(lambda () exp))
(define (cons-stream a b)
(cons a
(delay b)))
With this used in cons-stream
, infinite stream still illegal.
Such data structure reminds me of recursive function in whose definition self-call is legal (in compiling) within or without an actual exit.
Why is it illegal for a value to reference itself? Even the reference has been delayed?
Could infinite stream supported by other programming language?
If not, is it about the way processor deal with assembly language? The data stack stuff?
回答1:
When you make a procedure the arguments are already evaluated when you start the body of the procedure. Thus delay
would not do anything since it's already computed at that time. cons-stream
need to be a macro.
DrRacket is not one language implementation. It's an IDE that supports lots of languages. One of them is a SICP compatibility language. I mananged to run your code without errors using this code in DrRacket:
#!planet neil/sicp
(define ones
(cons-stream 1 ones))
(define (integers-starting-from n)
(cons-stream n
(integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
It works like a charm. The default language in DrRacket, #!racket
, has streams too, but the names are different:
#!racket
(define ones
(stream-cons 1 ones))
(define (integers-starting-from n)
(stream-cons n
(integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
However for Scheme you should use SRFI-41 that you can use from both #!racket
(using (require srfi/41)
) and #!r6rs
(and R7RS-large when it's finished)
(import (rnrs)
(srfi :41))
(define ones
(stream-cons 1 ones))
(define (integers-starting-from n)
(stream-cons n
(integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))
To roll your own SICP streams in both #!racket
and #!r6rs
you can use define-syntax
(define-syntax stream-cons
(syntax-rules ()
((_ a d) (cons a (delay d)))))
Note that RSFI-41 and rackets own stream library for #!racket
delays both values in a stream-cons
and not just the tail as in SICP version here.
回答2:
It's because the expression in a define
is evaluated before the name is bound to a value. It tries to evaluate (cons-stream 1 ones)
before ones
is defined, causing an error.
The reason this works fine for functions is that the body of a function is not evaluated when the function is. That is, to evaluate (lambda (x) (f x))
, the language returns a function without looking at its body. Since
(define (f x) (f x))
is syntax sugar for defining a lambda, the same logic applies.
Did you define cons-stream
yourself as above? If you make cons-stream
a normal function it won't work properly. Since Scheme is strict by default, arguments are evaluated before the function gets called. If cons-stream
is a normal function, b
would get evaluated completely before being passed to delay
, making you hit an infinite loop.
cons-stream
in SICP is a "special form" rather than a function, which means that it can control how its arguments are evaluated.
If you use the stream-cons
and other stream-
operations built into Racket, you'd get the behavior you want.
Finally, some other languages do allow values to reference themselves. A great example is Haskell, where this works because everything is lazy by default. Here's a Haskell snippet defining ones
to be an infinite list of ones. (Since everything is lazy, Haskell lists behave just like Scheme streams):
ones = 1 : ones
回答3:
This definition works in Racket:
(define ones
(cons-stream 1 ones))
…As long as you provide a delayed implementation of cons-stream
as a special form, which is the whole point of section 3.5 in SICP:
(define-syntax cons-stream
(syntax-rules ()
((_ head tail)
(cons head (delay tail)))))
回答4:
Adding to the answers above, to make sure the code run, you should also define delay
like this:
(define-syntax delay
(syntax-rules ()
((_ exp)
(lambda () exp))))
delay
as well as cons-stream
has to be defined as a macro.
In another option, you could just call the delay
predefined in Racket rather than building a new one.
But don't do this:
(define (delay exp)
(lambda () exp))
The compiler would take your definition, thus the program crashes in evaluation:
Interactions disabled
来源:https://stackoverflow.com/questions/31896117/why-is-it-legal-in-a-function-definition-to-make-self-call-but-illegal-for-a-val