Section
of Chapter
introduced a
number of Darwin built-in data types. A complete list of built-in
types can be found in Table
.
We have also seen how elements from different types can be included
in array and set data structures.
The array classics from the previous section was an example of such a
heterogeneous data structure.
> ReadProgram('Sample/arrays'):
> print(classics);
[
[Joseph Felsenstein,
Phylogenies from molecular sequences: inference and reliability,
Annual Revue of Genetics, 1988, 22, 521--565]
[Smith, Temple F. and Waterman, Michael S.,
Identification of common molecular subsequences, J. Mol. Biol., 1981, 147,
195--197]
[Dayhoff, Margaret O. and Schwartz, R. M. and Orcutt, B. C.,
A model for evolutionary change in proteins,
Atlas of Protein Sequence and Structure, 1978, 5, 345--352]
[Needleman, S. B. and Wunsch, C. D., A general method applicable to the search \
for similarities in the amino acid sequence of two proteins, J. Mol. Biol., 1970
, 48, 443--453]
]
However, as the diversity of
data in our programs become large, types such as list and set become
increasingly insufficient for maintaining clean, readable programs.
Even for the relatively small example of classics, the onus lies
on the programmer's shoulders to remember which field corresponds to
which item of information.
Most programming languages, including Darwin, offer some
manner for creating structured data types to
eleviate these problems.
A structured data type in Darwin is built using functions. The best way to learn how to define a structured type in Darwin is through examples. Let us begin by defining a new type nonnegint (non-negative integer) which consists of posint (positive integers) and the value 0.
> nonnegint := proc( x : {0, posint})
> return(noeval(nonnegint(x)));
> end:
This function accepts any argument which is either of type posint or has value 0. The type checking is done within the
formal parameter list. The body of the function simply returns an
unevaluated copy of the the function nonnegint with the
parameter x. The command noeval (no evaluation)
delays the evaluation of its argument. Darwin simply returns the
parameter of noeval as an object of type name. We can
define variables of type nonnegint by calling the function with
the appropriate argument. Observe what happens when the argument is
not of the correct type.
> a := nonnegint(0);
a := nonnegint(0)
> b := nonnegint(5);
b := nonnegint(5)
> c := nonnegint(1388293823);
c := nonnegint(1388293823)
> d := nonnegint(-1);
nonnegint expects a 1st argument, x:{0,posint}, found: -1
Error, invalid arguments
A variable (an instance) of a structured type is simply an unevaluated
procedure call in Darwin. There are two ways a procedure call can
remain unevaluated.
The first is through the use of the noeval
command described above. When we wrap the procedure call in a noeval command, Darwin just returns the procedure call and contents
as is.
> delayed := noeval(factorial(5));
delayed := factorial(5)
> frustrated := noeval(print('I so desparately want to simplify'));
frustrated := print(I so desparately want to simplify)
The second way a procedure call will remain unevaluated is when the procedure name is undefined in the scope. 5.1
> any := thing_goes(1, 2, 3);
any := thing_goes(1,2,3)
> I := have_unlimited('freedom');
I := have_unlimited(freedom)
> to_do := as_I_like(abc, 123, ['a', 'list'] );
to_do := as_I_like(abc,123,[a, list])
There are no procedures named thing_goes, have_unlimited
and as_I_like defined so Darwin simply assigns the unevaluated
name and parameters to the variables any, I and to_do.
What type of data do these structured types allow? The answer is,
literally, anything.
The variables which we create in this manner are assigned the type uneval (the values assigned to them look like unevaluated procedure calls).
> type(any, uneval); true > type(I, uneval); true > type(to_do, uneval); true
The manner in which one goes about definining structured types may seem a little abstract and, if you are an experienced programmer, much different than what you are used to with languages such as C or PASCAL. But bear with us: this method of structured type creation turns out to be extremely versatile. As in our example nonnegint, we can place an unlimited amount of code in the procedure which defines a type. This allows us to perform an arbitrary amount of type checking and manipulation of data.
Darwin comes equipped with several built-in structured types.
Table
contains a sample of the most used
structures. Chapter
- Iteration and
Recursion shows an application using the Tree structure. The
remaining entries are explored in greater depth
in Part
- Darwin and Problems from Biochemistry.
|
1.1
|