Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A Scheme puzzle

2 views
Skip to first unread message

Mario Latendresse

unread,
Apr 8, 2002, 2:45:07 PM4/8/02
to
Suppose we have a 0 arity function f and a function g defined as follow.

(define (g f n)
(let loop ((i 0) (s 0))
(if (< i n)
(loop (+ i 1) (+ (f) s))
s)))

Function g makes n calls to f, cumulating its returned values.

Function g is called several times, let say in some other loop.

(let loop2 ((j 0))
...
... (g f j) ...
(loop2 (+ j 1))
... )

We want to change f such that on its first call by g, f does some
initialization. Only on the first call by g should an initialization
be done by f. For example, f opens a temporary file, setup a random
number generator, etc. Note that on a second call to g, another
initialization should be done by f.

We do not want to change g, or the code calling it, like the term
(g f j) above.

In essence, we only want to modify f to do its initialization without
modifying the code that calls f.

The puzzle is: Is there a way to do that in Scheme?

Note: the following code does not work since when f is called a second time
by g, no new initialization will be done.

;; The old definition of f.
(define fprime
(lambda ()
... ))

;; The new f
(define f
(let ((initialized? #f))
(lambda ()
(if initialized?
(fprime)
(begin ;; initialization block
...
(set! initialized? #t)
(fprime)
)))))

Joe Marshall

unread,
Apr 8, 2002, 3:39:09 PM4/8/02
to

"Mario Latendresse" <late...@cs.rice.edu> wrote in message
news:c53ab82e.02040...@posting.google.com...

> Suppose we have a 0 arity function f and a function g
>
> We want to change f such that on its first call by g, f does some
> initialization. Only on the first call by g should an initialization
> be done by f. For example, f opens a temporary file, setup a random
> number generator, etc. Note that on a second call to g, another
> initialization should be done by f.
>
> We do not want to change g, or the code calling it, like the term
> (g f j) above.
>
> In essence, we only want to modify f to do its initialization without
> modifying the code that calls f.
>
> The puzzle is: Is there a way to do that in Scheme?
>

There is no way to do it in any language without modifying
G in *some* way. There is no way for F to know if G has
returned a value, so it cannot know if the current call is
a `new' one.

Jim White

unread,
Apr 9, 2002, 9:02:15 PM4/9/02
to
"Mario Latendresse" <late...@cs.rice.edu> wrote in message
news:c53ab82e.02040...@posting.google.com...
> We want to change f such that on its first call by g, f does some
> initialization. Only on the first call by g should an initialization
> be done by f. For example, f opens a temporary file, setup a random
> number generator, etc. Note that on a second call to g, another
> initialization should be done by f.
>
> We do not want to change g, or the code calling it, like the term
> (g f j) above.
> ...

That is a logical impossibility. You must change g. In fact you don't have
to change f at all. The exception to this would be to assume some runtime
specific code which examines the call stack so that f can hook g's
invocation frame.

jim


Eli Barzilay

unread,
Apr 10, 2002, 9:56:27 AM4/10/02
to
late...@cs.rice.edu (Mario Latendresse) writes:

> We do not want to change g, or the code calling it, like the term
> (g f j) above.

Others already said that this is impossible, but you can cheat your
way through by modifying g after it is defined:

(set! g
(let ((orig-g g))
(lambda args
(initializa-f)
(apply orig-g args))))

It does change `g', but not its original definition.

--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://www.barzilay.org/ Maze is Life!

Mario Latendresse

unread,
Apr 17, 2002, 2:38:22 PM4/17/02
to
Somehow it is indeed impossible to solve this puzzle as Joe Marshall
and Jim White points out. The trick of Eli Barzilay could also be
done, as it (indirectly) modifies the definition of g.

Yet I find this example instructive. In practice this situation arises
often. A (pure) function is modified to maintain a state. In that case
it becomes necessary to analyze all components that use that function
and modify the code accordingly.

For the example, the function f should do its initialization at the
point where g is called or right before several iterations is done
using f by g. This is the concept of closure creation, or creation of
an object.

The promotion of a function to an object (closure) can no longer be
done without revising the code.

It is true that the concept of closure embodies the concept of object.
Yet in a object oriented language, the programmer is made concious of
the need of a ``new object'' and write the code accordingly: In our
case, the programmer would have requested a new f, in g, before doing
all iterations. A Scheme programmer does not think that way (and often
refuses to think that way) and does not program as precisely.

-- Mario

Eli Barzilay

unread,
Apr 17, 2002, 3:22:59 PM4/17/02
to
late...@cs.rice.edu (Mario Latendresse) writes:

> It is true that the concept of closure embodies the concept of
> object. Yet in a object oriented language, the programmer is made
> concious of the need of a ``new object'' and write the code
> accordingly: In our case, the programmer would have requested a new
> f, in g, before doing all iterations. A Scheme programmer does not
> think that way (and often refuses to think that way) and does not
> program as precisely.

But if you put it this way, then you're not really taling about
changing

(define (g ...)
(... (f ...) ...))

so it will initialize f -- you talk about creating a closure as an
object which is something every Scheme programmer knows how to do. It
will look like this:

(define (g ...)
(let ((my-f (f ...init-values...)))
(... (my-f ...) ...)))

so f is a function that will create the closure which will be used:

(define (f ...init-args...)
(let ((blah ...) ...)
(lambda (f's-args) ...)))

0 new messages