Local procedures with logo
Return
With UCBLOGO all procedures are global.
But variables may be local .
To implement local procedures we can attach procedures to variables.
The actuai procedure name is genereted by gensym which genereates unique names.
The procedures are global but there access is local.
A procedure name is created as local variable and the procedure localdefine generates a unique
procedure name using gensym and attach the procedure's name to the local variable.
The procedure localrun execute the procedure with its parameters.
When the procedure is no more used we erase its local name.
to Testlocalprocedure
local "proc
localDefine "proc [[x][pr :x]]
localrun [ proc [here proc1]]
embededproc
er :proc
end
To see that the same procedure name is local we define a different procedure with the same name
inside the procedure embededproc which is called by Testlocalprocedure.
to embededproc
local "proc
localdefine "proc [[x][(pr "----- :x "-----)]]
localrun [proc [here proc2]]
er :proc
end
? testlocalprocedure
here proc1
----- here proc2 -----
If we don't define a local procedure in embededproc the procedure defined in Testlocalprocedure (which is under
the scope of the variable proc) is used.
to embededproc
,local "proc
,localdefine "proc [[x][(pr "----- :x "-----)]]
localrun [proc [here proc2]]
er :proc
end
? testlocalprocedure
here proc1
here proc2
Procedure as black box
Locales procedures are needed to hide procedures used as tool in a main procedure.
We are only interested only the action of the main procedure.The tools are defined as local procedures
inside the body of the main procedure.
Example from Structure and Interpretation of Computer Programs, 1.1.8 Procedures as Black-Box Abstractions.
Internal definitions and block structure
We have one kind of name isolation available to us so far: The formal parameters of a procedure are local inside the body of the procedure.
The square-root program illustrates another way in which we would like to control the use of names.
The existing program consists of separate procedures:
(define (sqrt x)
(sqrt-iter 1.0 x))
(define (sqrt-iter guess x)
(if (good-enough? guess x)
guess
(sqrt-iter (improve guess x) x)))
(define (good-enough? guess x)
(< (abs (- (square guess) x)) 0.001))
(define (improve guess x)
(average guess (/ x guess)))
The problem with this program is that the only procedure that is important to users of sqrt is sqrt.
The other procedures (sqrt-iter, good-enough?, and improve) only clutter up their minds.
They may not define any other procedure called good-enough? as part of another program to work together
with the square-root program, because sqrt needs it. The problem is especially severe in the construction
of large systems by many separate programmers.
For example, in the construction of a large library of numerical procedures, many numerical functions are
computed as successive approximations and thus might have procedures named good-enough? and improve
as auxiliary procedures.
We would like to localize the subprocedures, hiding them inside sqrt so that sqrt could coexist with other successive approximations, each having its own private good-enough? procedure. To make this possible,
we allow
a procedure to have internal definitions that are local to that procedure. For example, in the square-root
problem we can write
(define (sqrt x)
(define (good-enough? guess x)
(< (abs (- (square guess) x)) 0.001))
(define (improve guess x)
(average guess (/ x guess)))
(define (sqrt-iter guess x)
(if (good-enough? guess x)
guess
(sqrt-iter (improve guess x) x)))
(sqrt-iter 1.0 x))
Such nesting of definitions, called block structure, is basically the right solution to the simplest name-packaging problem. But there is a better idea lurking here.
(define (sqrt x)
(define (good-enough? guess)
(< (abs (- (square guess) x)) 0.001))
(define (improve guess)
(average guess (/ x guess)))
(define (sqrt-iter guess)
(if (good-enough? guess)
guess
(sqrt-iter (improve guess))))
(sqrt-iter 1.0))
UCBLogo version:
to sqrtnew :x
;Assuming square abs average already defined
local "good.enough
localdefine "good.enough [[guess x] [op (abs (( square :guess) - :x ) )< 0.0001 ]]
local "improve
localdefine "improve [[guess x] [op average :guess (:x / :guess) ]]
local "sqrt.iter
localdefine "sqrt.iter [[guess x] [op ifelse (localrun [good.enough :guess :x] ) [ :guess][ localrun [sqrt.iter (localrun [improve :guess :x] ):x]]]]
localmake "temp ( round ( 10000 * ( localrun [sqrt.iter 1.0 :x] ) ) ) / 10000
er thing "sqrt.iter
er thing "good.enough
er thing "improve
op :temp
end
? show procedures
[abs average buryall created embededproc erlocals localdefine localrun mytest showt sqrtiter sqrtnew sqrtnew2
square testlocalprocedure unburyall]
? pr sqrtnew 2
1.4142
? show procedures
[abs average buryall created embededproc erlocals localdefine localrun mytest showt sqrtiter sqrtnew sqrtnew2
square testlocalprocedure unburyall]
?
We see that after running sqrtnew the procedures are the same then before
In addition to internalizing the definitions of the auxiliary procedures, we can simplify them.
Since x is bound in the definition of sqrt, the procedures good-enough?, improve, and sqrt-iter, which are
defined internally to sqrt, are in the scope of x.
Thus, it is not necessary to pass x explicitly to each of these procedures. Instead, we allow x to be a
free variable in the internal definitions, as shown below.
Then x gets its value from the argument with which the enclosing procedure sqrt is called.
This discipline is called lexical scoping.27
(define (sqrt x)
(define (good-enough? guess)
(< (abs (- (square guess) x)) 0.001))
(define (improve guess)
(average guess (/ x guess)))
(define (sqrt-iter guess)
(if (good-enough? guess)
guess
(sqrt-iter (improve guess))))
(sqrt-iter 1.0))
Ucblogo version:
to sqrtnewwithoutx :x
;Assuming square abs average already defined
local "good.enough
localdefine "good.enough [[guess ] [op (abs (( square :guess) - :x ) )< 0.0001 ]]
local "improve
localdefine "improve [[guess ] [op average :guess (:x / :guess) ]]
local "sqrt.iter
localdefine "sqrt.iter [[guess x] [op ifelse (localrun [good.enough :guess ] ) [ :guess][ localrun [sqrt.iter (localrun [improve :guess ] ):x]]]]
localmake "temp ( round ( 10000 * ( localrun [sqrt.iter 1.0 :x] ) ) ) / 10000
er thing "sqrt.iter
er thing "good.enough
er thing "improve
op :temp
end
Return
.