The function sqrt
is our first example of a process defined by a set of mutually
defined functions.
Notice that the
declaration of
sqrt_iter
is
recursive; that is, the
function
is defined in terms of itself. The idea of being able to define a
function
in terms of itself may be disturbing; it may seem unclear how such a
circular definition could make sense at all, much less
specify a well-defined process to be carried out by a computer. This will
be addressed more carefully in
section 1.2. But first
let's consider some other important points illustrated by the
sqrt example.
Observe that the problem of computing square roots breaks up naturally
into a number of subproblems:
how to tell whether a guess is good
enough, how to improve a guess, and so on. Each of these tasks is
accomplished by a separate
function.
The entire sqrt program can be viewed as a
cluster of
functions
(shown in figure 1.4)
that mirrors the decomposition of the problem into subproblems.
Figure 1.4 Functional decomposition of the
sqrt program.
The importance of this
decomposition strategy is not simply that one
is dividing the program into parts. After all, we could take any
large program and divide it into parts—the first ten lines, the next
ten lines, the next ten lines, and so on. Rather, it is crucial that
each
function
accomplishes an identifiable task that can be used as a module in defining
other
functions.
For example, when we define the
is_good_enough function
in terms of square, we are able to
regard the square
function
as a
black box. We are not at that moment concerned with
how the
function
computes its result, only with the fact that it computes the
square. The details of how the square is computed can be suppressed,
to be considered at a later time. Indeed, as far as the
is_good_enough function
is concerned, square is not quite a
function
but rather an abstraction of a
function,
a so-called
functional abstraction.
At this level of abstraction, any
function
that computes the square is equally good.
Thus, considering only the values they return, the following two
functions
squaring a number should be indistinguishable. Each takes a numerical
argument and produces the square of that number as the value.
function square(x) {
return math_exp(double(math_log(x)));
}
function double(x) {
return x + x;
}
So a
function
should be able to suppress detail. The users of the
function
may not have written the
function
themselves, but may have obtained it from another programmer as a
black box. A user should not need to know how the
function
is implemented in order to use it.
One detail of a
function's
implementation that should not matter to the user of the
function
is the implementer's choice of names for the
function's parameters.
Thus, the following
functions
should not be distinguishable:
This principle—that the meaning of a
function
should be independent of the parameter names used by its
author—seems on the surface to be self-evident, but its
consequences are profound. The simplest consequence is that the
parameter names of a
function
must be local to the body of the
function.
For example, we used square
in the declaration of
is_good_enough
in our square-root
function:
The intention of the author of
is_good_enough
is to determine if the square of the first argument is within a given
tolerance of the second argument. We see that the author of
is_good_enough
used the name guess to refer to the
first argument and x to refer to the
second argument. The argument of square
is guess. If the author of
square used x
(as above) to refer to that argument, we see that the
x in
is_good_enough
must be a different x than the one
in square. Running the
function
square must not affect the value
of x that is used by
is_good_enough,
because that value of x may be needed by
is_good_enough
after square is done computing.
is_good_enough
の作者の意図は、最初の引数の二乗が 2 番目の引数の所定の許容範囲内にあるかどうかを判定することです。
is_good_enough
の作者は、最初の引数を参照するのに名前 guess を、2 番目の引数を参照するのに x を使いました。square の引数は guess です。もし square の作者がその引数を参照するのに(上のように)x を使ったとすると、
is_good_enough
の中の x は square の中の x とは別のものでなければなりません。
関数
square の実行は、
is_good_enough
が使う x の値に影響を与えてはなりません。square の計算が終わった後に、その x の値が
is_good_enough
に必要かもしれないからです。
If the parameters were not local to the bodies of their respective
functions,
then the parameter x in
square could be confused with the parameter
x in
is_good_enough,
and the behavior of
is_good_enough
would depend upon which version of square
we used. Thus, square would not be the
black box we desired.
もしパラメータがそれぞれの
関数
の本体にローカルでなかったら、square のパラメータ x が
is_good_enough
のパラメータ x と混同される可能性があり、
is_good_enough
の動作は、square のどのバージョンを使うかに依存してしまいます。そうなると、square は望んだブラックボックスではなくなります。
A
parameter of a function
has a very special role in the
function declaration,
in that it doesn't matter what name the
parameter has. Such a name is called
bound, and we say that the function declaration
binds its
parameters.
The meaning of a
function declaration is unchanged if a bound name
is consistently renamed throughout the
declaration.
If a
name
is not bound, we say that it is
free. The set of
statements
for which a binding
declares
a name is called the
scope of that name. In a
function declaration, the bound names
declared as the
parameters of the function
have the body of the
function
as their scope.
In the
declaration of is_good_enough
above,
guess and
x are
bound
names
but
abs
and square are free.
The meaning of
is_good_enough
should be independent of the names we choose for
guess and
x so long as they are distinct and
different from
abs
and square. (If we renamed
guess to
abs we would have introduced a bug by
capturing the
name
abs.
It would have changed from free to bound.) The meaning of
is_good_enough
is not independent of the
choice of its free names,
however. It surely depends upon the fact
(external to this declaration)
that the name abs refers to a function
for computing the absolute value of a number.
The function is_good_enough
will compute a different function if we substitute
math_cos
(the primitive cosine function)
for abs in its
declaration.
We have one kind of name isolation available to us so far:
The parameters of a function
are local to the body of the
function.
The square-root program illustrates another way in which we would like to
control the use of names.
The existing program consists of separate
functions:
現在のプログラムは個別の
関数
で構成されています。
function sqrt(x) {
return sqrt_iter(1, x);
}
function sqrt_iter(guess, x) {
return is_good_enough(guess, x)
? guess
: sqrt_iter(improve(guess, x), x);
}
function is_good_enough(guess, x) {
return abs(square(guess) - x) < 0.001;
}
function improve(guess, x) {
return average(guess, x / guess);
}
The problem with this program is that the only
function
that is important to users of sqrt is
sqrt. The other
functions
(sqrt_iter,
is_good_enough,
and improve) only clutter up their minds.
They may not
declare any other function
called
is_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
functions,
many numerical functions are computed as successive approximations and
thus might have
functions
named
is_good_enough
and improve as auxiliary
functions.
We would like to localize the
subfunctions,
hiding them inside sqrt so that
sqrt could coexist with other
successive approximations, each having its own private
is_good_enough function.
To make this possible, we allow a
function
to have
internal declarations that are local to that
function.
For example, in the square-root problem we can write
function sqrt(x) {
function is_good_enough(guess, x) {
return abs(square(guess) - x) < 0.001;
}
function improve(guess, x) {
return average(guess, x / guess);
}
function sqrt_iter(guess, x) {
return is_good_enough(guess, x)
? guess
: sqrt_iter(improve(guess, x), x);
}
return sqrt_iter(1, x);
}
Any matching pair of braces designates a block, and
declarations inside the block are local to the block.
Such nesting of
declarations,
called block structure, is basically the right solution to the
simplest name-packaging problem. But there is a better idea lurking here.
In addition to internalizing the
declarations of the auxiliary functions,
we can simplify them. Since x is bound in the
declaration
of sqrt, the
functions
is_good_enough,
improve, and
sqrt_iter,
which are declared internally to
sqrt, are in the scope of
x. Thus, it is not necessary to pass
x explicitly to each of these
functions.
Instead, we allow x to be a free
name
in the internal
declarations,
as shown below. Then x gets its value from
the argument with which the enclosing
function
sqrt is called. This discipline is called
lexical scoping.
対応する波括弧のペアはブロックを指定し、ブロック内の宣言はそのブロックにローカルです。
このような
宣言
の入れ子はブロック構造と呼ばれ、最も単純な名前のパッケージング問題に対する基本的に正しい解決策です。しかし、ここにはもっと良いアイデアが隠れています。補助
関数の宣言
を内部化するだけでなく、それらを簡素化できるのです。x は sqrt の
宣言
で束縛されているので、sqrt の内部で
宣言された
関数
is_good_enough、
improve、
sqrt_iter
は x のスコープ内にあります。したがって、これらの
関数
のそれぞれに x を明示的に渡す必要はありません。代わりに、以下に示すように、内部
宣言
で x を自由な
名前
にします。すると x は、外側の
関数
sqrt が呼ばれたときの引数から値を得ます。この規律は
レキシカルスコーピングと呼ばれます。
The idea of block structure originated with the programming language
Algol 60. It appears in most advanced programming languages and is an
important tool for helping to organize the construction of large programs.
It
is not even clear which of these
functions
is a more efficient implementation. This depends upon the hardware
available. There are machines for which the obvious
implementation is the less efficient one. Consider a machine that has
extensive tables of logarithms and antilogarithms stored in a very
efficient manner.
Lexical scoping dictates that free
names in a function
are taken to refer to bindings made by enclosing
function declarations;
that is, they are looked up in
the environment in which the
function was declared.
We will see how this works in detail in chapter 3 when we
study environments and the detailed behavior of the interpreter.
Embedded
declarations must come first in a function
body.
The management is not responsible for the consequences of running programs
that intertwine
declaration
and use; see also
footnotes undefined
and undefined
in section 1.3.2.