amiga-e/amigae33a/E_v3.3a/Docs/BeginnersGuide/Introduction.guide

1682 lines
63 KiB
Plaintext

@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Introduction to Amiga E"
@Next "Understanding a Simple Program"
@Prev "Contents.guide/main"
@Toc "Contents.guide/main"
Introduction to Amiga E
***********************
To interact with your Amiga you need to speak a language it understands.
Luckily, there is a wide choice of such languages, each of which fits a
particular need. For instance, BASIC (in most of its flavours) is simple
and easy to learn, and so is ideal for beginners. Assembly, on the other
hand, requires a lot of effort and is quite tedious, but can produce the
fastest programs so is generally used by commercial programmers. These
are two extremes and most businesses and colleges use C or
Pascal/Modula-2, which try to strike a balance between simplicity and
speed.
E programs look very much like Pascal or Modula-2 programs, but E is
based more closely on C. Anyone familiar with these languages will easily
learn E, only really needing to get to grips with E's unique features and
those borrowed from other languages. This guide is aimed at people who
haven't done much programming and may be too trivial for competent
programmers, who should find the `E Reference Manual' more than adequate
(although some of the later sections offer different explanations to the
`Reference Manual', which may prove useful).
Part One (this part) goes through some of the basics of the E language
and programming in general. Part Two delves deeper into E, covering the
more complex topics and the unique features of E. Part Three goes through
a few example programs, which are a bit longer than the examples in the
other Parts. Finally, Part Four contains the Appendices, which is where
you'll find some other, miscellaneous information.
@{" A Simple Program " Link "A Simple Program" }
@ENDNODE
@NODE "A Simple Program" "A Simple Program"
@Toc "main"
A Simple Program
================
If you're still reading you're probably desperate to do some
programming in E but you don't know how to start. We'll therefore jump
straight in the deep end with a small example. You'll need to know two
things before we start: how to use a text editor and the Shell/CLI.
@{" The code " Link "The code" }
@{" Compilation " Link "Compilation" }
@{" Execution " Link "Execution" }
@ENDNODE
@NODE "The code" "The code"
@Next "Compilation"
@Toc "A Simple Program"
The code
--------
Enter the following lines of code into a text editor and save it as the
file @{b }simple.e@{ub } (taking care to copy each line accurately). (Just type the
characters shown, and at the end of each line press the RETURN or ENTER
key.)
PROC main()
WriteF('My first program')
ENDPROC
Don't try to do anything different to the code, yet: the case of the
letters in each word is significant and the funny characters are important.
If you're a real beginner you might have difficulty finding the '
character. On my GB keyboard it's on the big key in the top left-hand
corner directly below the ESC key. On a US and most European keyboards
it's two to the right of the L key, next to the ; key. (If you don't have
your keyboard set up properly then you find that keys don't produce the
same characters that are printed on them--especially when use use the
shift key. In this case it will probably behave like a US keyboard,
although you should really fix this and set it up properly--see the
manuals that came with your Amiga.)
@ENDNODE
@NODE "Compilation" "Compilation"
@Next "Execution"
@Prev "The code"
@Toc "A Simple Program"
Compilation
-----------
Once the file is saved (preferably in the RAM disk, since it's only a
small program), you can use the E compiler to turn it into an executable
program. All you need is the file @{b }ec@{ub } in your @{b }C:@{ub } directory or somewhere
else on your search path (advanced users note: we don't need the @{b }Emodules:@{ub }
assignment because we aren't using any modules). Assuming you have this
and you have a Shell/CLI running, enter the following at the prompt after
changing directory to where you saved your new file:
ec simple
If all's well you should be greeted, briefly, by the E compiler. If
anything went wrong then double-check the contents of the file @{b }simple.e@{ub },
that your CLI is in the same directory as this file, and that the program
@{b }ec@{ub } is in your @{b }C:@{ub } directory (or on your search path).
@ENDNODE
@NODE "Execution" "Execution"
@Prev "Compilation"
@Toc "A Simple Program"
Execution
---------
Once everything is working you can run your first program by entering
the following at the CLI prompt:
simple
As a help here's the complete transcript of the whole compilation and
execution process (the CLI prompt, below, is the bit of text beginning
with @{b }1.@{ub } and ending in @{b }>@{ub }):
1.System3.0:> cd ram:
1.Ram Disk:> ec simple
Amiga E Compiler/Assembler/Linker/PP v3.2e registered (c) '91-95 Wouter
lexical analysing ...
parsing and compiling ...
no errors
1.Ram Disk:> simple
My first program1.Ram Disk:>
Your display should be something similar if it's all worked. Notice how
the output from the program runs into the prompt (the last line). We'll
fix this soon.
@ENDNODE
@NODE "Understanding a Simple Program" "Understanding a Simple Program"
@Next "Variables and Expressions"
@Prev "main"
@Toc "Contents.guide/main"
Understanding a Simple Program
******************************
To understand the example program we need to understand quite a few
things. The observant amongst you will have noticed that all it does is
print out a message, and that message was part of a line we wrote in the
program. The first thing to do is see how to change this message.
@{" Changing the Message " Link "Changing the Message" }
@{" Procedures " Link "Procedures" }
@{" Parameters " Link "Parameters" }
@{" Strings " Link "Strings" }
@{" Style Reuse and Readability " Link "Style Reuse and Readability" }
@{" The Simple Program " Link "The Simple Program" }
@ENDNODE
@NODE "Changing the Message" "Changing the Message"
@Next "Procedures"
@Toc "Understanding a Simple Program"
Changing the Message
====================
Edit the file so that line contains a different message between the two
' characters and compile it again using the same procedure as before.
Don't use any ' characters except those around the message. If all went
well, when you run the program again it should produce a different message.
If something went wrong, compare the contents of your file with the
original and make sure the only difference is the message between the '
characters.
@{" Tinkering with the example " Link "Tinkering with the example" }
@{" Brief overview " Link "Brief overview" }
@ENDNODE
@NODE "Tinkering with the example" "Tinkering with the example"
@Next "Brief overview"
@Toc "Changing the Message"
Tinkering with the example
--------------------------
Simple tinkering is a good way to learn for yourself so it is
encouraged on these simple examples. Don't stray too far, though, and if
you start getting confused return to the proper example pretty sharpish!
@ENDNODE
@NODE "Brief overview" "Brief overview"
@Prev "Tinkering with the example"
@Toc "Changing the Message"
Brief overview
--------------
We'll look in detail at the important parts of the program in the
following sections, but we need first to get a glimpse of the whole
picture. Here's a brief description of some fundamental concepts:
@{b }*@{ub } @{i }Procedures:@{ui } We defined a procedure called @{b }main@{ub } and used the
(built-in) procedure @{b }WriteF@{ub }. A procedure can be thought of as a
small program with a name.
@{b }*@{ub } @{i }Parameters:@{ui } The message in parentheses after @{b }WriteF@{ub } in our
program is the parameter to @{b }WriteF@{ub }. This is the data which the
procedure should use.
@{b }*@{ub } @{i }Strings:@{ui } The message we passed to @{b }WriteF@{ub } was a series of
characters enclosed in ' characters. This is known as a @{fg shine }string@{fg text }.
@ENDNODE
@NODE "Procedures" "Procedures"
@Next "Parameters"
@Prev "Changing the Message"
@Toc "Understanding a Simple Program"
Procedures
==========
As mentioned above, a procedure can be thought of as a small program
with a name. In fact, when an E program is run the procedure called @{b }main@{ub }
is executed. Therefore, if your E program is going to do anything you
must define a @{b }main@{ub } procedure. Other (built-in or user-defined) procedures
may be run (or @{fg shine }called@{fg text }) from this procedure (as we did @{b }WriteF@{ub } in the
example). For instance, if the procedure @{b }fred@{ub } calls the procedure @{b }barney@{ub }
the code (or mini-program) associated with @{b }barney@{ub } is executed. This may
involve calls to other procedures, and when the execution of this code is
complete the next piece of code in the procedure @{b }fred@{ub } is executed (and
this is generally the next line of the procedure). When the end of the
procedure @{b }main@{ub } has been reached the program has finished. However, lots
can happen between the beginning and end of a procedure, and sometimes the
program may never get to finish. Alternatively, the program may @{fg shine }crash@{fg text },
causing strange things to happen to your computer.
@{" Procedure Definition " Link "Procedure Definition" }
@{" Procedure Execution " Link "Procedure Execution" }
@{" Extending the example " Link "Extending the example" }
@ENDNODE
@NODE "Procedure Definition" "Procedure Definition"
@Next "Procedure Execution"
@Toc "Procedures"
Procedure Definition
--------------------
Procedures are defined using the keyword @{b }PROC@{ub }, followed by the new
procedure's name (starting with a lowercase letter), a description of the
parameters it takes (in parentheses), a series of lines forming the code
of the procedure and then the keyword @{b }ENDPROC@{ub }. Look at the example
program again to identify the various parts. See @{"The code" Link "The code" }.
@ENDNODE
@NODE "Procedure Execution" "Procedure Execution"
@Next "Extending the example"
@Prev "Procedure Definition"
@Toc "Procedures"
Procedure Execution
-------------------
Procedures can be called (or executed) from within the code part of
another procedure. You do this by giving its name, followed by some data
in parentheses. Look at the call to @{b }WriteF@{ub } in the example program. See
@{"The code" Link "The code" }.
@ENDNODE
@NODE "Extending the example" "Extending the example"
@Prev "Procedure Execution"
@Toc "Procedures"
Extending the example
---------------------
Here's how we could change the example program to define another
procedure:
PROC main()
WriteF('My first program')
fred()
ENDPROC
PROC fred()
WriteF('...slightly improved')
ENDPROC
This may seem complicated, but in fact it's very simple. All we've done
is define a second procedure called @{b }fred@{ub } which is just like the original
program--it outputs a message. We've @{fg shine }called@{fg text } this procedure in the @{b }main@{ub }
procedure just after the line which outputs the original message.
Therefore, the message in @{b }fred@{ub } is output after this message. Compile the
program as before and run it so you don't have to take my word for it.
@ENDNODE
@NODE "Parameters" "Parameters"
@Next "Strings"
@Prev "Procedures"
@Toc "Understanding a Simple Program"
Parameters
==========
Generally we want procedures to work with particular data. In our
example we wanted the @{b }WriteF@{ub } procedure to work on a particular message.
We passed the message as a @{fg shine }parameter@{fg text } (or @{fg shine }argument@{fg text }) to @{b }WriteF@{ub } by
putting it between the parentheses (the @{b }(@{ub } and @{b })@{ub } characters) that follow
the procedure name. When we called the @{b }fred@{ub } procedure, however, we did
not require it to use any data so the parentheses were left empty.
When defining a procedure we define how much and what type of data we
want it to work on, and when calling a procedure we give the specific data
it should use. Notice that the procedure @{b }fred@{ub } (like the procedure @{b }main@{ub })
has empty parentheses in its definition. This means that the procedure
cannot be given any data as parameters when it is called. Before we can
define our own procedure that takes parameters we must learn about
variables. We'll do this in the next chapter. See
@{"Global and local variables" Link "Global and local variables" }.
@ENDNODE
@NODE "Strings" "Strings"
@Next "Style Reuse and Readability"
@Prev "Parameters"
@Toc "Understanding a Simple Program"
Strings
=======
A series of characters between two ' characters is known as a string.
Almost any character can be used in a string, although the \\ and '
characters have a special meaning. For instance, a linefeed is denoted by
the two characters @{b }\\n@{ub }. We now know how to stop the message running into
the prompt. Change the program to be:
PROC main()
WriteF('My first program\\n')
fred()
ENDPROC
PROC fred()
WriteF('...slightly improved\\n')
ENDPROC
Compile it as before, and run it. You should notice that the messages now
appear on lines by themselves, and the second message is separated from
the prompt which follows it. We have therefore cured the linefeed problem
we spotted earlier (see @{"Execution" Link "Execution" }).
@ENDNODE
@NODE "Style Reuse and Readability" "Style Reuse and Readability"
@Next "The Simple Program"
@Prev "Strings"
@Toc "Understanding a Simple Program"
Style, Reuse and Readability
============================
The example has grown into two procedures, one called @{b }main@{ub } and one
called @{b }fred@{ub }. However, we could get by with only one procedure:
PROC main()
WriteF('My first program\\n')
WriteF('...slightly improved\\n')
ENDPROC
What we've done is replace the call to the procedure @{b }fred@{ub } with the code
it represents (this is called @{fg shine }inlining@{fg text } the procedure). In fact, almost
all programs can be easily re-written to eliminate all but the @{b }main@{ub }
procedure. However, splitting a program up using procedures normally
results in more readable code. It is also helpful to name your procedures
so that their function is apparent, so our procedure @{b }fred@{ub } should probably
have been named @{b }message@{ub } or something similar. A well-written program in
this style can read just like English (or any other spoken language).
Another reason for having procedures is to reuse code, rather than
having to write it out every time you use it. Imagine you wanted to print
the same, long message fairly often in your program--you'd either have to
write it all out every time, or you could write it once in a procedure and
call this procedure when you wanted the message printed. Using a
procedure also has the benefit of having only one copy of the message to
change, should it ever need changing.
@ENDNODE
@NODE "The Simple Program" "The Simple Program"
@Prev "Style Reuse and Readability"
@Toc "Understanding a Simple Program"
The Simple Program
==================
The simple program should now (hopefully) seem simple. The only bit
that hasn't been explained is the built-in procedure @{b }WriteF@{ub }. E has many
built-in procedures and later we'll meet some of them in detail. The
first thing we need to do, though, is manipulate data. This is really
what a computer does all the time--it accepts data from some source
(possibly the user), manipulates it in some way (possibly storing it
somewhere, too) and outputs new data (usually to a screen or printer).
The simple example program did all this, except the first two stages were
rather trivial. You told the computer to execute the compiled program
(this was some user input) and the real data (the message to be printed)
was retrieved from the program. This data was manipulated by passing it
as a parameter to @{b }WriteF@{ub }, which then did some clever stuff to print it on
the screen. To do our own manipulation of data we need to learn about
variables and expressions.
@ENDNODE
@NODE "Variables and Expressions" "Variables and Expressions"
@Next "Program Flow Control"
@Prev "Understanding a Simple Program"
@Toc "Contents.guide/main"
Variables and Expressions
*************************
Anybody who's done any school algebra will probably know what a
variable is--it's just a named piece of data. In algebra the data is
usually a number, but in E it can be all sorts of things (e.g., a string).
The manipulation of data like the addition of two numbers is known as an
@{fg shine }expression@{fg text }. The result of an expression can be used to build bigger
expressions. For instance, @{b }1+2@{ub } is an expression, and so is @{b }6-(1+2)@{ub }. The
good thing is you can use variables in place of data in expressions, so if
@{b }x@{ub } represents the number 1 and @{b }y@{ub } represents 5, then the expression @{b }y-x@{ub }
represents the number 4. In the next two sections we'll look at what kind
of variables you can define and what the different sorts of expressions
are.
@{" Variables " Link "Variables" }
@{" Expressions " Link "Expressions" }
@ENDNODE
@NODE "Variables" "Variables"
@Next "Expressions"
@Toc "Variables and Expressions"
Variables
=========
Variables in E can hold many different kinds of data (called @{fg shine }types@{fg text }).
However, before a variable can be used it must be defined, and this is
known as @{fg shine }declaring@{fg text } the variable. A variable declaration also decides
whether the variable is available for the whole program or just during the
code of a procedure (i.e., whether the variable is @{fg shine }global@{fg text } or @{fg shine }local@{fg text }).
Finally, the data stored in a variable can be changed using @{fg shine }assignments@{fg text }.
The following sections discuss these topics in slightly more detail.
@{" Variable types " Link "Variable types" }
@{" Variable declaration " Link "Variable declaration" }
@{" Assignment " Link "Assignment" }
@{" Global and local variables " Link "Global and local variables" }
@{" Changing the example " Link "Changing the example" }
@ENDNODE
@NODE "Variable types" "Variable types"
@Next "Variable declaration"
@Toc "Variables"
Variable types
--------------
In E a variable is a storage place for data (and this storage is part
of the Amiga's RAM). Different kinds of data may require different
amounts of storage. However, data can be grouped together in @{fg shine }types@{fg text }, and
two pieces of data from the same type require the same amount of storage.
Every variable has an associated type and this dictates the maximum amount
of storage it uses. Most commonly, variables in E store data from the
type @{b }LONG@{ub }. This type contains the integers from -2,147,483,648 to
2,147,483,647, so is normally more than sufficient. There are other
types, such as @{b }INT@{ub } and @{b }LIST@{ub }, and more complex things to do with types, but
for now knowing about @{b }LONG@{ub } is enough.
@ENDNODE
@NODE "Variable declaration" "Variable declaration"
@Next "Assignment"
@Prev "Variable types"
@Toc "Variables"
Variable declaration
--------------------
Variables must be declared before they can be used. They are declared
using the @{b }DEF@{ub } keyword followed by a (comma-separated) list of the names of
the variables to be declared. These variables will all have type @{b }LONG@{ub }
(later we will see how to declare variables with other types). Some
examples will hopefully make things clearer:
DEF x
DEF a, b, c
The first line declares the single variable @{b }x@{ub }, whilst the second declares
the variables @{b }a@{ub }, @{b }b@{ub } and @{b }c@{ub } all in one go.
@ENDNODE
@NODE "Assignment" "Assignment"
@Next "Global and local variables"
@Prev "Variable declaration"
@Toc "Variables"
Assignment
----------
The data stored by variables can be changed and this is normally done
using @{fg shine }assignments@{fg text }. An assignment is formed using the variable's name
and an expression denoting the new data it is to store. The symbol @{b }:=@{ub }
separates the variable from the expression. For example, the following
code stores the number two in the variable @{b }x@{ub }. The left-hand side of the
@{b }:=@{ub } is the name of the variable to be affected (@{b }x@{ub } in this case) and the
right-hand side is an expression denoting the new value (simply the number
two in this case).
x := 2
The following, more complex example uses the value stored in the variable
before the assignment as part of the expression for the new data. The
value of the expression on the right-hand side of the @{b }:=@{ub } is the value
stored in the variable @{b }x@{ub } plus one. This value is then stored in @{b }x@{ub },
over-writing the previous data. (So, the overall effect is that @{b }x@{ub } is
incremented.)
x := x + 1
This may be clearer in the next example which does not change the data
stored in @{b }x@{ub }. In fact, this piece of code is just a waste of CPU time,
since all it does is look up the value stored in @{b }x@{ub } and store it back there!
x := x
@ENDNODE
@NODE "Global and local variables" "Global and local variables"
@Next "Changing the example"
@Prev "Assignment"
@Toc "Variables"
Global and local variables (and procedure parameters)
-----------------------------------------------------
There are two kinds of variable: @{fg shine }global@{fg text } and @{fg shine }local@{fg text }. Data stored by
global variables can be read and changed by all procedures, but data
stored by local variables can only be accessed by the procedure to which
they are local. Global variables must be declared before the first
procedure definition. Local variables are declared within the procedure
to which they are local (i.e., between the @{b }PROC@{ub } and @{b }ENDPROC@{ub }). For
example, the following code declares a global variable @{b }w@{ub } and local
variables @{b }x@{ub } and @{b }y@{ub }.
DEF w
PROC main()
DEF x
x:=2
w:=1
fred()
ENDPROC
PROC fred()
DEF y
y:=3
w:=2
ENDPROC
The variable @{b }x@{ub } is local to the procedure @{b }main@{ub }, and @{b }y@{ub } is local to @{b }fred@{ub }.
The procedures @{b }main@{ub } and @{b }fred@{ub } can read and alter the value of the global
variable @{b }w@{ub }, but @{b }fred@{ub } cannot read or alter the value of @{b }x@{ub } (since that
variable is local to @{b }main@{ub }). Similarly, @{b }main@{ub } cannot read or alter @{b }y@{ub }.
The local variables of one procedure are, therefore, completely
different to the local variables of another procedure. For this reason
they can share the same names without confusion. So, in the above
example, the local variable @{b }y@{ub } in @{b }fred@{ub } could have been called @{b }x@{ub } and the
program would have done exactly the same thing.
DEF w
PROC main()
DEF x
x:=2
w:=1
fred()
ENDPROC
PROC fred()
DEF x
x:=3
w:=2
ENDPROC
This works because the @{b }x@{ub } in the assignment in @{b }fred@{ub } can refer only to the
local variable @{b }x@{ub } of @{b }fred@{ub } (the @{b }x@{ub } in @{b }main@{ub } is local to @{b }main@{ub } so cannot be
accessed from @{b }fred@{ub }).
If a local variable for a procedure has the same name as a global
variable then in the rest of the procedure the name refers only to the
local variable. Therefore, the global variable cannot be accessed in the
procedure, and this is called @{fg shine }descoping@{fg text } the global variable.
The parameters of a procedure are local variables for that procedure.
We've seen how to pass values as parameters when a procedure is called
(the use of @{b }WriteF@{ub } in the example), but until now we haven't been able to
define a procedure which takes parameters. Now we know a bit about
variables we can have a go:
DEF y
PROC onemore(x)
y:=x+1
ENDPROC
This isn't a complete program so don't try to compile it. Basically,
we've declared a variable @{b }y@{ub } (which will be of type @{b }LONG@{ub }) and a procedure
@{b }onemore@{ub }. The procedure is defined with a parameter @{b }x@{ub }, and this is just
like a (local) variable declaration. When @{b }onemore@{ub } is called a parameter
must be supplied, and this value is stored in the (local) variable @{b }x@{ub }
before execution of @{b }onemore@{ub }'s code. The code stores the value of @{b }x@{ub } plus
one in the (global) variable @{b }y@{ub }. The following are some examples of
calling @{b }onemore@{ub }:
onemore(120)
onemore(52+34)
onemore(y)
A procedure can be defined to take any number of parameters. Below,
the procedure @{b }addthem@{ub } is defined to take two parameters, @{b }a@{ub } and @{b }b@{ub }, so it
must therefore be called with two parameters. Notice that values stored
by the parameter variables (@{b }a@{ub } and @{b }b@{ub }) can be changed within the code of the
procedure, since they are just like local variables for the procedure.
(The only real difference between local and parameter variables is that
parameter variables are initialised with the values supplied as parameters
when the procedure is called.)
DEF y
PROC addthem(a, b)
a:=a+2
y:=a*b
ENDPROC
The following are some examples of calling @{b }addthem@{ub }:
addthem(120,-20)
addthem(52,34)
addthem(y,y)
Global variables are, by default, initialised to zero. Parameter
variables are, of course, initialised by the actual values passed as
parameters when a procedure is called. However, local variables are not
initialised. This means that a local variable will contain a fairly
random value when the code of a procedure is first executed. It is the
responsibility of the programmer to ensure no assumptions are made about
the value of local variables before they have been initialised. The
obvious way to initialise a local variable is using an assignment, but
there is also a way of giving an initialisation value as part of the
declaration (see @{"Initialised Declarations" Link "MoreExpressions.guide/Initialised Declarations" }). Initialisation of variables
is often very important, and is a common reason why programs go wrong.
@ENDNODE
@NODE "Changing the example" "Changing the example"
@Prev "Global and local variables"
@Toc "Variables"
Changing the example
--------------------
Before we change the example we must learn something about @{b }WriteF@{ub }. We
already know that the characters @{b }\\n@{ub } in a string mean a linefeed.
However, there are several other important combinations of characters in a
string, and some are special to procedures like @{b }WriteF@{ub }. One such
combination is @{b }\\d@{ub }, which is easier to describe after we've seen the
changed example.
PROC main()
WriteF('My first program\\n')
fred()
ENDPROC
PROC fred()
WriteF('...brought to you by the number \\d\\n', 236)
ENDPROC
You might be able to guess what happens, but compile it and try it out
anyway. If everything's worked you should see that the second message
prints out the number that was passed as the second parameter to @{b }WriteF@{ub }.
That's what the @{b }\\d@{ub } combination does--it marks the place in the string
where the number should be printed. Here's the output the example should
generate:
My first program
...brought to you by the number 236
Try this next change:
PROC main()
WriteF('My first program\\n')
fred()
ENDPROC
PROC fred()
WriteF('...the number \\d is quite nice\\n', 16)
ENDPROC
This is very similar, and just shows that the @{b }\\d@{ub } really does mark the
place where the number is printed. Again, here's the output it should
generate:
My first program
...the number 16 is quite nice
We'll now try printing two numbers.
PROC main()
WriteF('My first program\\n')
fred()
ENDPROC
PROC fred()
WriteF('...brought to you by the numbers \\d and \\d\\n', 16, 236)
ENDPROC
Because we're printing two numbers we need two lots of @{b }\\d@{ub }, and we need to
supply two numbers as parameters in the order in which we want them to be
printed. The number 16 will therefore be printed before the word `and'
and before the number 236. Here's the output:
My first program
...brought to you by the numbers 16 and 236
We can now make a big step forward and pass the numbers as parameters
to the procedure @{b }fred@{ub }. Just look at the differences between this next
example and the previous one.
PROC main()
WriteF('My first program\\n')
fred(16, 236)
ENDPROC
PROC fred(a,b)
WriteF('...brought to you by the numbers \\d and \\d\\n', a,b)
ENDPROC
This time we pass the (local) variables @{b }a@{ub } and @{b }b@{ub } to @{b }WriteF@{ub }. This is
exactly the same as passing the values they store (which is what the
previous example did), and so the output will be the same. In the next
section we'll manipulate the variables by doing some arithmetic with @{b }a@{ub } and
@{b }b@{ub }, and get @{b }WriteF@{ub } to print the results.
@ENDNODE
@NODE "Expressions" "Expressions"
@Prev "Variables"
@Toc "Variables and Expressions"
Expressions
===========
The E language includes the normal mathematical and logical operators.
These operators are combined with values (usually in variables) to give
@{fg shine }expressions@{fg text } which yield new values. The following sections discuss this
topic in more detail.
@{" Mathematics " Link "Mathematics" }
@{" Logic and comparison " Link "Logic and comparison" }
@{" Precedence and grouping " Link "Precedence and grouping" }
@ENDNODE
@NODE "Mathematics" "Mathematics"
@Next "Logic and comparison"
@Toc "Expressions"
Mathematics
-----------
All the standard mathematical operators are supported in E. You can do
addition, subtraction, multiplication and division. Other functions such
as sine, modulus and square-root can also be used as they are part of the
Amiga system libraries, but we only need to know about simple mathematics
at the moment. The @{b }+@{ub } character is used for addition, @{b }-@{ub } for subtraction, @{b }*@{ub }
for multiplication (it's the closest you can get to a multiplication sign
on a keyboard without using the letter @{b }x@{ub }), and @{b }/@{ub } for division (be careful
not to confuse the @{b }\\@{ub} used in strings with @{b }/@{ub } used for division). The
following are examples of expressions:
1+2+3+4
15-5
5*2
330/33
-10+20
3*3+1
Each of these expressions yields ten as its result. The last example is
very carefully written to get the precedence correct (see
@{"Precedence and grouping" Link "Precedence and grouping" }).
All the above expressions use integer operators, so they manipulate
integers, giving integers as results. @{fg shine }Floating-point@{fg text } numbers are also
supported by E, but using them is quite complicated (see
@{"Floating-Point Numbers" Link "FloatingPoint.guide/main" }). (Floating-point numbers can represent both very
small fractions and very large integers, but they have a limited accuracy,
i.e., a limited number of @{i }significant@{ui } digits.)
@ENDNODE
@NODE "Logic and comparison" "Logic and comparison"
@Next "Precedence and grouping"
@Prev "Mathematics"
@Toc "Expressions"
Logic and comparison
--------------------
Logic lies at the very heart of a computer. They rarely guess what to
do next; instead they rely on hard facts and precise reasoning. Consider
the password protection on most games. The computer must decide whether
you entered the correct number or word before it lets you play the game.
When you play the game it's constantly making decisions: did your laser
hit the alien?, have you got any lives left?, etc. Logic controls the
operation of a program.
In E, the constants @{b }TRUE@{ub } and @{b }FALSE@{ub } represent the truth values true and
false (respectively), and the operators @{b }AND@{ub } and @{b }OR@{ub } are the standard logic
operators. The comparison operators are @{b }=@{ub } (equal to), @{b }>@{ub } (greater than), @{b }<@{ub }
(less than), @{b }>=@{ub } (greater than or equal to), @{b }<=@{ub } (less than or equal to) and
@{b }<>@{ub } (not equal to). All the following expressions are true:
TRUE
TRUE AND TRUE
TRUE OR FALSE
1=1
2>1
3<>0
And these are all false:
FALSE
TRUE AND FALSE
FALSE OR FALSE
0=2
2<1
(2<1) AND (-1=0)
The last example must use parentheses. We'll see why in the next section
(it's to do with precedence, again).
The truth values @{b }TRUE@{ub } and @{b }FALSE@{ub } are actually numbers. This is how the
logic system works in E. @{b }TRUE@{ub } is the number -1 and @{b }FALSE@{ub } is zero. The
logic operators @{b }AND@{ub } and @{b }OR@{ub } expect such numbers as their parameters. In
fact, the @{b }AND@{ub } and @{b }OR@{ub } operators are really bit-wise operators (see
@{"Bitwise AND and OR" Link "MoreExpressions.guide/Bitwise AND and OR" }), so most of the time any non-zero number is taken to
be @{b }TRUE@{ub }. It can sometimes be convenient to rely on this knowledge,
although most of the time it is preferable (and more readable) to use a
slightly more explicit form. Also, these facts can cause a few subtle
problems as we shall see in the next section.
@ENDNODE
@NODE "Precedence and grouping" "Precedence and grouping"
@Prev "Logic and comparison"
@Toc "Expressions"
Precedence and grouping
-----------------------
At school most of us are taught that multiplications must be done
before additions in a sum. In E it's different--there is no operator
precedence, and the normal order in which the operations are performed is
left-to-right, just like the expression is written. This means that
expressions like @{b }1+3*3@{ub } do not give the results a mathematician might
expect. In fact, @{b }1+3*3@{ub } represents the number 12 in E. This is because the
addition, @{b }1+3@{ub }, is done before the multiplication, since it occurs before
the multiplication. If the multiplication were written before the
addition it would be done first (like we would normally expect).
Therefore, @{b }3*3+1@{ub } represents the number 10 in E and in school mathematics.
To overcome this difference we can use parentheses to group the
expression. If we'd written @{b }1+(3*3)@{ub } the result would be 10. This is
because we've forced E to do the multiplication first. Although this may
seem troublesome to begin with, it's actually a lot better than learning a
lot of rules for deciding which operator is done first (in C this can be a
real pain, and you usually end up writing the brackets in just to be
sure!).
The logic examples above contained the expression:
(2<1) AND (-1=0)
This expression was false. If we'd left the parentheses out, it would
have been:
2<1 AND -1=0
This is actually interpreted the same as:
((2<1) AND -1) = 0
Now the number -1 shouldn't really be used to represent a truth value with
@{b }AND@{ub }, but we do know that @{b }TRUE@{ub } is the number -1, so E will make sense of
this and the E compiler won't complain. We will soon see how @{b }AND@{ub } and @{b }OR@{ub }
really work (see @{"Bitwise AND and OR" Link "MoreExpressions.guide/Bitwise AND and OR" }), but for now we'll just work out what
E would calculate for this expression:
1. Two is not less than one so @{b }2<1@{ub } can be replaced by @{b }FALSE@{ub }.
(FALSE AND -1) = 0
2. @{b }TRUE@{ub } is -1 so we can replace -1 by @{b }TRUE@{ub }.
(FALSE AND TRUE) = 0
3. @{b }FALSE AND TRUE@{ub } is @{b }FALSE@{ub }.
(FALSE) = 0
4. @{b }FALSE@{ub } is really the number zero, so we can replace it with zero.
0 = 0
5. Zero is equal to zero, so the expression is @{b }TRUE@{ub }.
TRUE
So E calculates the expression to be true. But the original expression
(with parentheses) was false. Bracketing is therefore very important! It
is also very easy to do correctly.
@ENDNODE
@NODE "Program Flow Control" "Program Flow Control"
@Next "Summary"
@Prev "Variables and Expressions"
@Toc "Contents.guide/main"
Program Flow Control
********************
A computer program often needs to repeatedly execute a series of
statements or execute different statements according to the result of some
decision. For example, a program to print all the numbers between one and
a thousand would be very long and tedious to write if each print statement
had to be given individually--it would be much better to use a variable
and repeatedly print its value and increment it.
Another aspect of flow control is choosing between different pieces of
code to execute. For instance, if something goes wrong a program may need
to decide whether to continue or print an error message and stop--this
part of a program is a typical example of a conditional block.
@{" Conditional Block " Link "Conditional Block" }
@{" Loops " Link "Loops" }
@ENDNODE
@NODE "Conditional Block" "Conditional Block"
@Next "Loops"
@Toc "Program Flow Control"
Conditional Block
=================
There are two kinds of conditional block: @{b }IF@{ub } and @{b }SELECT@{ub }. Examples of
these blocks are given below as fragments of E code (i.e., the examples
are not complete E programs).
IF x>0
x:=x+1
WriteF('Increment: x is now \\d\\n', x)
ELSEIF x<0
x:=x-1
WriteF('Decrement: x is now \\d\\n', x)
ELSE
WriteF('Zero: x is 0\\n')
ENDIF
In the above @{b }IF@{ub } block, the first part checks if the value of @{b }x@{ub } is greater
than zero, and, if it is, @{b }x@{ub } is incremented and the new value is printed
(with a message saying it was incremented). The program will then skip
the rest of the block, and will execute the statements which follow the
@{b }ENDIF@{ub }. If, however, @{b }x@{ub } it is not greater than zero the @{b }ELSEIF@{ub } part is
checked, so if @{b }x@{ub } is less than zero it will be decremented and printed, and
the rest of the block is skipped. If @{b }x@{ub } is not greater than zero and not
less than zero the statements in the @{b }ELSE@{ub } part are executed, so a message
saying @{b }x@{ub } is zero is printed. The @{b }IF@{ub } conditional is described in more
detail below.
@{" IF block " Link "IF block" }
@{" IF expression " Link "IF expression" }
SELECT x
CASE 0
WriteF('x is zero\\n')
CASE 10
WriteF('x is ten\\n')
CASE -2
WriteF('x is -2\\n')
DEFAULT
WriteF('x is not zero, ten or -2\\n')
ENDSELECT
The @{b }SELECT@{ub } block is similar to the @{b }IF@{ub } block--it does different things
depending on the value of @{b }x@{ub }. However, @{b }x@{ub } is only checked against specific
values, given in the series of @{b }CASE@{ub } statements. If it is not any of these
values the @{b }DEFAULT@{ub } part is executed.
There's also a variation on the @{b }SELECT@{ub } block (known as the @{b }SELECT..OF@{ub }
block) which matches ranges of values and is quite fast. The two kinds of
@{b }SELECT@{ub } block are described in more detail below.
@{" SELECT block " Link "SELECT block" }
@{" SELECT..OF block " Link "SELECT..OF block" }
@ENDNODE
@NODE "IF block" "IF block"
@Next "IF expression"
@Toc "Conditional Block"
@{b }IF@{ub } block
--------
The @{b }IF@{ub } block has the following form (the bits like @{fg shine }expression@{fg text } are
descriptions of the kinds of E code which is allowed at that point--they
are not proper E code):
IF @{fg shine }expressionA@{fg text }
@{fg shine }statementsA@{fg text }
ELSEIF @{fg shine }expressionB@{fg text }
@{fg shine }statementsB@{fg text }
ELSE
@{fg shine }statementsC@{fg text }
ENDIF
This block means:
@{b }*@{ub } If @{fg shine }expressionA@{fg text } is true (i.e., represents @{b }TRUE@{ub } or any non-zero
number) the code denoted by @{fg shine }statementsA@{fg text } is executed.
@{b }*@{ub } If @{fg shine }expressionA@{fg text } is false (i.e., represents @{b }FALSE@{ub } or zero) and
@{fg shine }expressionB@{fg text } is true the @{fg shine }statementsB@{fg text } part is executed.
@{b }*@{ub } If both @{fg shine }expressionA@{fg text } and @{fg shine }expressionB@{fg text } are false the @{fg shine }statementsC@{fg text }
part is executed.
There does not need to be an @{b }ELSE@{ub } part but if one is present it must be
the last part (immediately before the @{b }ENDIF@{ub }). Also, there can be any
number of @{b }ELSEIF@{ub } parts between the @{b }IF@{ub } and @{b }ELSE@{ub } parts.
An alternative to this vertical form (where each part is on a separate
line) is the horizontal form:
IF @{fg shine }expression@{fg text } THEN @{fg shine }statementA@{fg text } ELSE @{fg shine }statementB@{fg text }
This has the disadvantage of no @{b }ELSEIF@{ub } parts and having to cram everything
onto a single line. Notice the presence of the @{b }THEN@{ub } keyword to separate the
@{fg shine }expression@{fg text } and @{fg shine }statementA@{fg text }. This horizontal form is closely related to
the @{b }IF@{ub } expression, which is described below (see @{"IF expression" Link "IF expression" }).
To help make things clearer here are a number of E code fragments which
illustrate the allowable @{b }IF@{ub } blocks:
IF x>0 THEN x:=x+1 ELSE x:=0
IF x>0
x:=x+1
ELSE
x:=0
ENDIF
IF x=0 THEN WriteF('x is zero\\n')
IF x=0
WriteF('x is zero\\n')
ENDIF
IF x<0
Write('Negative x\\n')
ELSEIF x>2000
Write('Too big x\\n')
ELSEIF (x=2000) OR (x=0)
Write('Worrying x\\n')
ENDIF
IF x>0
IF x>2000
WriteF('Big x\\n')
ELSE
WriteF('OK x\\n')
ENDIF
ELSE
IF x<-800 THEN WriteF('Small x\\n') ELSE Write('Negative OK x')
ENDIF
In the last example there are @{fg shine }nested@{fg text } @{b }IF@{ub } blocks (i.e., an @{b }IF@{ub } block within
an @{b }IF@{ub } block). There is no ambiguity in which @{b }ELSE@{ub } or @{b }ELSEIF@{ub } parts belong
to which @{b }IF@{ub } block because the beginning and end of the @{b }IF@{ub } blocks are
clearly marked. For instance, the first @{b }ELSE@{ub } line can be interpreted only
as being part of the innermost @{b }IF@{ub } block.
As a matter of style the conditions on the @{b }IF@{ub } and @{b }ELSEIF@{ub } parts should
not @{fg shine }overlap@{fg text } (i.e., at most one of the conditions should be true). If
they do, however, the first one will take precedence. Therefore, the
following two fragments of E code do the same thing:
IF x>0
WriteF('x is bigger than zero\\n')
ELSEIF x>200
WriteF('x is bigger than 200\\n')
ELSE
WriteF('x is too small\\n')
ENDIF
IF x>0
WriteF('x is bigger than zero\\n')
ELSE
WriteF('x is too small\\n')
ENDIF
The @{b }ELSEIF@{ub } part of the first fragment checks whether @{b }x@{ub } is greater than 200.
But, if it is, the check in the @{b }IF@{ub } part would have been true (@{b }x@{ub } is
certainly greater than zero if it's greater than 200), and so only the
code in the @{b }IF@{ub } part is executed. The whole @{b }IF@{ub } block behaves as if the
@{b }ELSEIF@{ub } was not there.
@ENDNODE
@NODE "IF expression" "IF expression"
@Next "SELECT block"
@Prev "IF block"
@Toc "Conditional Block"
@{b }IF@{ub } expression
-------------
@{b }IF@{ub } is such a commonly used construction that there is also an @{b }IF@{ub }
expression. The @{b }IF@{ub } block is a statement and it controls which lines of
code are executed, whereas the @{b }IF@{ub } expression is an expression and it
controls its own value. For example, the following @{b }IF@{ub } block:
IF x>0
y:=x+1
ELSE
y:=0
ENDIF
can be written more succinctly using an @{b }IF@{ub } expression:
y:=(IF x>0 THEN x+1 ELSE 0)
The parentheses are unnecessary but they help to make the example more
readable. Since the @{b }IF@{ub } block is just choosing between two assignments to
@{b }y@{ub } it isn't really the lines of code that are different (they are both
assignments), rather it is the values that are assigned to @{b }y@{ub } that are
different. The @{b }IF@{ub } expression makes this similarity very clear. It
chooses the @{i }value@{ui } to be assigned in just the same way that the @{b }IF@{ub } block
choose the @{i }assignment@{ui }.
The @{b }IF@{ub } expression has the following form:
IF @{fg shine }exp@{fg text } THEN @{fg shine }expA@{fg text } ELSE @{fg shine }expB@{fg text }
As you can see, @{b }IF@{ub } expressions are written like the horizontal form of the
@{b }IF@{ub } block. However, there must be an @{b }ELSE@{ub } part and there can be no @{b }ELSEIF@{ub }
parts. This means that the expression will always have a value (either
@{fg shine }expA@{fg text } or @{fg shine }expB@{fg text }, depending on the value of @{fg shine }exp@{fg text }), and it isn't cluttered
with lots of cases.
Don't worry too much about @{b }IF@{ub } expressions, since there are only useful
in a handful of cases and can always be rewritten as a more wordy @{b }IF@{ub } block.
Having said that they are very elegant and a lot more readable than the
equivalent @{b }IF@{ub } block.
@ENDNODE
@NODE "SELECT block" "SELECT block"
@Next "SELECT..OF block"
@Prev "IF expression"
@Toc "Conditional Block"
@{b }SELECT@{ub } block
------------
The @{b }SELECT@{ub } block has the following form:
SELECT @{fg shine }variable@{fg text }
CASE @{fg shine }expressionA@{fg text }
@{fg shine }statementsA@{fg text }
CASE @{fg shine }expressionB@{fg text }
@{fg shine }statementsB@{fg text }
DEFAULT
@{fg shine }statementsC@{fg text }
ENDSELECT
The value of the selection variable (denoted by @{fg shine }variable@{fg text } in the @{b }SELECT@{ub }
part) is compared with the value of the expression in each of the @{b }CASE@{ub }
parts in turn. If there's a match, the statements in the (first) matching
@{b }CASE@{ub } part are executed. There can be any number of @{b }CASE@{ub } parts between the
@{b }SELECT@{ub } and @{b }DEFAULT@{ub } parts. If there is no match, the statements in the
@{b }DEFAULT@{ub } part are executed. There does not need to be a @{b }DEFAULT@{ub } part but
if one is present it must be the last part (immediately before the
@{b }ENDSELECT@{ub }).
It should be clear that @{b }SELECT@{ub } blocks can be rewritten as @{b }IF@{ub } blocks,
with the checks on the @{b }IF@{ub } and @{b }ELSEIF@{ub } parts being equality checks on the
selection variable. For example, the following code fragments are
equivalent:
SELECT x
CASE 22
WriteF('x is 22\\n')
CASE (y+z)/2
WriteF('x is (y+x)/2\\n')
DEFAULT
WriteF('x isn't anything significant\\n')
ENDSELECT
IF x=22
WriteF('x is 22\\n')
ELSEIF x=(y+z)/2
WriteF('x is (y+x)/2\\n')
ELSE
WriteF('x isn't anything significant\\n')
ENDIF
Notice that the @{b }IF@{ub } and @{b }ELSEIF@{ub } parts come from the @{b }CASE@{ub } parts, the @{b }ELSE@{ub }
part comes from the @{b }DEFAULT@{ub } part, and the order of the parts is preserved.
The advantage of the @{b }SELECT@{ub } block is that it's much easier to see that the
value of @{b }x@{ub } is being tested all the time, and also we don't have to keep
writing @{b }x=@{ub } in the checks.
@ENDNODE
@NODE "SELECT..OF block" "SELECT..OF block"
@Prev "SELECT block"
@Toc "Conditional Block"
@{b }SELECT..OF@{ub } block
----------------
The @{b }SELECT..OF@{ub } block is a bit more complicated than the normal @{b }SELECT@{ub }
block, but can be very useful. It has the following form:
SELECT @{fg shine }maxrange@{fg text } OF @{fg shine }expression@{fg text }
CASE @{fg shine }constA@{fg text }
@{fg shine }statementsA@{fg text }
CASE @{fg shine }constB1@{fg text } TO @{fg shine }constB2@{fg text }
@{fg shine }statementsB@{fg text }
CASE @{fg shine }range1@{fg text }, @{fg shine }range2@{fg text }
@{fg shine }statementsC@{fg text }
DEFAULT
@{fg shine }statementsD@{fg text }
ENDSELECT
The value to be matched is @{fg shine }expression@{fg text }, which can be any expression,
not just a variable like in the normal @{b }SELECT@{ub } block. However, the
@{fg shine }maxrange@{fg text }, @{fg shine }constA@{fg text }, @{fg shine }constB1@{fg text } and @{fg shine }constB2@{fg text } must all be explicit numbers,
i.e., constants (see @{"Constants" Link "Constants.guide/main" }). @{fg shine }maxrange@{fg text } must be a positive constant
and the other constants must all be between zero and @{fg shine }maxrange@{fg text } (including
zero but excluding @{fg shine }maxrange@{fg text }).
The @{b }CASE@{ub } values to be matched are specified using @{fg shine }ranges@{fg text }. A simple
range is a single constant (the first @{b }CASE@{ub } above). The more general range
is shown in the second @{b }CASE@{ub }, using the @{b }TO@{ub } keyword (@{fg shine }constB2@{fg text } must be
greater than @{fg shine }constB1@{fg text }). A general @{b }CASE@{ub } in the @{b }SELECT..OF@{ub } block can
specify a number of possible ranges to match against by separating each
range with a comma, as in the third @{b }CASE@{ub } above. For example, the
following @{b }CASE@{ub } lines are equivalent and can be used to match any number
from one to five (inclusive):
CASE 1 TO 5
CASE 1, 2, 3, 4, 5
CASE 1 TO 3, 3 TO 5
CASE 1, 2 TO 3, 4, 5
CASE 1, 5, 2, 4, 3
CASE 2 TO 3, 5, 1, 4
If the value of the @{fg shine }expression@{fg text } is less than zero, greater than or
equal to @{fg shine }maxrange@{fg text }, or it does not match any of the constants in the @{b }CASE@{ub }
ranges, then the statements in the @{b }DEFAULT@{ub } part are executed. Otherwise
the statements in the first matching @{b }CASE@{ub } part are executed. As in the
normal @{b }SELECT@{ub } block, there does not need to be a @{b }DEFAULT@{ub } part.
The following @{b }SELECT..OF@{ub } block prints the (numeric) day of the month
nicely:
SELECT 32 OF day
CASE 1, 21, 31
WriteF('The \\dst day of the month\\n', day)
CASE 2, 22
WriteF('The \\dnd day of the month\\n', day)
CASE 3, 23
WriteF('The \\drd day of the month\\n', day)
CASE 4 TO 20, 24 TO 30
WriteF('The \\dth day of the month\\n', day)
DEFAULT
WriteF('Error: invalid day=\\d\\n', day)
ENDSELECT
The @{fg shine }maxrange@{fg text } for this block is 32, since 31 is the maximum of the values
used in the @{b }CASE@{ub } parts. If the value of @{b }day@{ub } was 100, for instance, then
the statements in the @{b }DEFAULT@{ub } part would be executed, signalling an
invalid day.
This example can be rewritten as an @{b }IF@{ub } block:
IF (day=1) OR (day=21) OR (day=31)
WriteF('The \\dst day of the month\\n', day)
ELSEIF (day=2) OR (day=22)
WriteF('The \\dnd day of the month\\n', day)
ELSEIF (day=3) OR (day=23)
WriteF('The \\drd day of the month\\n', day)
ELSEIF ((4<=day) AND (day<=20)) OR ((24<=day) AND (day<=30))
WriteF('The \\dth day of the month\\n', day)
ELSE
WriteF('Error: invalid day=\\d\\n', day)
ENDIF
The comma separating two ranges in the @{b }CASE@{ub } part has been replaced by an
@{b }OR@{ub } of two comparison expressions, and the @{b }TO@{ub } range has been replaced
by an @{b }AND@{ub } of two comparisons. (It is worth noticing the careful
bracketing of the resulting expressions.)
Clearly, the @{b }SELECT..OF@{ub } block is much more readable than the equivalent
@{b }IF@{ub } block. It is also a lot faster, mainly because none of the comparisons
present in @{b }IF@{ub } block have to be done in the @{b }SELECT..OF@{ub } version. Instead
the value to be matched is used to immediately locate the correct @{b }CASE@{ub }
part. However, it's not all good news: the @{fg shine }maxrange@{fg text } value directly
affects the size of compiled executable, so it is recommended that
@{b }SELECT..OF@{ub } blocks be used only with small @{fg shine }maxrange@{fg text } values. See the
`Reference Manual' for more details.
@ENDNODE
@NODE "Loops" "Loops"
@Prev "Conditional Block"
@Toc "Program Flow Control"
Loops
=====
Loops are all about making a program execute a series of statements
over and over again. Probably the simplest loop to understand is the @{b }FOR@{ub }
loop. There are other kinds of loops, but they are easier to understand
once we know how to use a @{b }FOR@{ub } loop.
@{" FOR loop " Link "FOR loop" }
@{" WHILE loop " Link "WHILE loop" }
@{" REPEAT..UNTIL loop " Link "REPEAT..UNTIL loop" }
@ENDNODE
@NODE "FOR loop" "FOR loop"
@Next "WHILE loop"
@Toc "Loops"
@{b }FOR@{ub } loop
--------
If you want to write a program to print the numbers one to 100 you can
either type each number and wear out your fingers, or you can use a single
variable and a small @{b }FOR@{ub } loop. Try compiling this E program (the space
after the @{b }\\d@{ub } in the string is needed to separate the printed numbers):
PROC main()
DEF x
FOR x:=1 TO 100
WriteF('\\d ', x)
ENDFOR
WriteF('\\n')
ENDPROC
When you run this you'll get all the numbers from one to 100 printed, just
like we wanted. It works by using the (local) variable @{b }x@{ub } to hold the
number to be printed. The @{b }FOR@{ub } loop starts off by setting the value of @{b }x@{ub }
to one (the bit that looks like an assignment). Then the statements
between the @{b }FOR@{ub } and @{b }ENDFOR@{ub } lines are executed (so the value of @{b }x@{ub } gets
printed). When the program reaches the @{b }ENDFOR@{ub } it increments @{b }x@{ub } and checks
to see if it is bigger than 100 (the limit we set with the @{b }TO@{ub } part). If
it is, the loop is finished and the statements after the @{b }ENDFOR@{ub } are
executed. If, however, it wasn't bigger than 100, the statements between
the @{b }FOR@{ub } and @{b }ENDFOR@{ub } lines are executed all over again, and this time @{b }x@{ub } is
one bigger since it has been incremented. In fact, this program does
exactly the same as the following program (the @{b }...@{ub } is not E code--it
stands for the 97 other @{b }WriteF@{ub } statements):
PROC main()
WriteF('\\d ', 1)
WriteF('\\d ', 2)
...
WriteF('\\d ', 100)
WriteF('\\n')
ENDPROC
The general form of the @{b }FOR@{ub } loop is as follows:
FOR @{fg shine }var@{fg text } := @{fg shine }expressionA@{fg text } TO @{fg shine }expressionB@{fg text } STEP @{fg shine }number@{fg text }
@{fg shine }statements@{fg text }
ENDFOR
The @{fg shine }var@{fg text } bit stands for the loop variable (in the example above this was
@{b }x@{ub }). The @{fg shine }expressionA@{fg text } bit gives the start value for the loop variable
and the @{fg shine }expressionB@{fg text } bit gives the last allowable value for it. The @{b }STEP@{ub }
part allows you to specify the value (given by @{fg shine }number@{fg text }) which is added to
the loop variable on each loop. Unlike the values given for the start and
end (which can be arbitrary expressions), the @{b }STEP@{ub } value must be a
constant (see @{"Constants" Link "Constants.guide/main" }). The @{b }STEP@{ub } value defaults to one if the @{b }STEP@{ub } part
is omitted (as in our example). Negative @{b }STEP@{ub } values are allowed, but in
this case the check used at the end of each loop is whether the loop
variable is @{i }less than@{ui } the value in the @{b }TO@{ub } part. Zero is not allowed as
the @{b }STEP@{ub } value.
As with the @{b }IF@{ub } block there is a horizontal form of a @{b }FOR@{ub } loop:
FOR @{fg shine }var@{fg text } := @{fg shine }expA@{fg text } TO @{fg shine }expB@{fg text } STEP @{fg shine }expC@{fg text } DO @{fg shine }statement@{fg text }
@ENDNODE
@NODE "WHILE loop" "WHILE loop"
@Next "REPEAT..UNTIL loop"
@Prev "FOR loop"
@Toc "Loops"
@{b }WHILE@{ub } loop
----------
The @{b }FOR@{ub } loop used a loop variable and checked whether that variable had
gone past its limit. A @{b }WHILE@{ub } loop allows you to specify your own loop
check. For instance, this program does the same as the program in the
previous section:
PROC main()
DEF x
x:=1
WHILE x<=100
WriteF('\\d ', x)
x:=x+1
ENDWHILE
WriteF('\\n')
ENDPROC
We've replaced the @{b }FOR@{ub } loop with an initialisation of @{b }x@{ub } and a @{b }WHILE@{ub } loop
with an extra statement to increment @{b }x@{ub }. We can now see the inner workings
of the @{b }FOR@{ub } loop and, in fact, this is exactly how the @{b }FOR@{ub } loop works.
It is important to know that our check, @{b }x<=100@{ub }, is done before the loop
statements are executed. This means that the loop statements might not
even be executed once. For instance, if we'd made the check @{b }x>=100@{ub } it
would be false at the beginning of the loop (since @{b }x@{ub } is initialised to one
in the assignment before the loop). Therefore, the loop would have
terminated immediately and execution would pass straight to the statements
after the @{b }ENDWHILE@{ub }.
Here's a more complicated example:
PROC main()
DEF x,y
x:=1
y:=2
WHILE (x<10) AND (y<10)
WriteF('x is \\d and y is \\d\\n', x, y)
x:=x+2
y:=y+2
ENDWHILE
ENDPROC
We've used two (local) variables this time. As soon as one of them is ten
or more the loop is terminated. A bit of inspection of the code reveals
that @{b }x@{ub } is initialised to one, and keeps having two added to it. It will,
therefore, always be an odd number. Similarly, @{b }y@{ub } will always be even.
The @{b }WHILE@{ub } check shows that it won't print any numbers which are greater
than or equal to ten. From this and the fact that @{b }x@{ub } starts at one and @{b }y@{ub }
at two we can decide that the last pair of numbers will be seven and eight.
Run the program to confirm this. It should produce the following output:
x is 1 and y is 2
x is 3 and y is 4
x is 5 and y is 6
x is 7 and y is 8
Like the @{b }FOR@{ub } loop, there is a horizontal form of the @{b }WHILE@{ub } loop:
WHILE @{fg shine }expression@{fg text } DO @{fg shine }statement@{fg text }
Loop termination is always a big problem. @{b }FOR@{ub } loops are guaranteed to
eventually reach their limit (if you don't mess with the loop variable,
that is). However, @{b }WHILE@{ub } loops (and all other loops) may go on forever
and never terminate. For example, if the loop check were @{b }1<2@{ub } it would
always be true and nothing the loop could do would prevent it being true!
You must therefore take care that your loops terminate in some way if you
want to program to finish. There is a sneaky way of terminating loops
using the @{b }JUMP@{ub } statement, but we'll ignore that for now.
@ENDNODE
@NODE "REPEAT..UNTIL loop" "REPEAT..UNTIL loop"
@Prev "WHILE loop"
@Toc "Loops"
@{b }REPEAT..UNTIL@{ub } loop
------------------
A @{b }REPEAT..UNTIL@{ub } loop is very similar to a @{b }WHILE@{ub } loop. The only
difference is where you specify the loop check, and when and how the check
is performed. To illustrate this, here's the program from the previous
two sections rewritten using a @{b }REPEAT..UNTIL@{ub } loop (try to spot the subtle
differences):
PROC main()
DEF x
x:=1
REPEAT
WriteF('\\d ', x)
x:=x+1
UNTIL x>100
WriteF('\\n')
ENDPROC
Just as in the @{b }WHILE@{ub } loop version we've got an initialisation of @{b }x@{ub } and an
extra statement in the loop to increment @{b }x@{ub }. However, this time the loop
check is specified at the end of the loop (in the @{b }UNTIL@{ub } part), and the
check is only performed at the end of each loop. This difference means
that the code in a @{b }REPEAT..UNTIL@{ub } loop will be executed at least once,
whereas the code in a @{b }WHILE@{ub } loop may never be executed. Also, the logical
sense of the check follows the English: a @{b }REPEAT..UNTIL@{ub } loop executes
@{i }until@{ui } the check is true, whereas the @{b }WHILE@{ub } loop executes @{i }while@{ui } the
check is true. Therefore, the @{b }REPEAT..UNTIL@{ub } loop executes while the check
is false! This may seem confusing at first, but just remember to read the
code as if it were English and you'll get the correct interpretation.
@ENDNODE
@NODE "Summary" "Summary"
@Next "Format.guide/main"
@Prev "Program Flow Control"
@Toc "Contents.guide/main"
Summary
*******
This is the end of Part One, which was hopefully enough to get you
started. If you've grasped the main concepts you are good position to
attack Part Two, which covers the E language in more detail.
This is probably a good time to look at the different parts of one of
the examples from the previous sections, since we've now used quite a bit
of E. The following examination uses the @{b }WHILE@{ub } loop example. Just to make
things easier to follow, each line has been numbered (don't try to compile
it with the line numbers on!).
1. PROC main()
2. DEF x,y
3. x:=1
4. y:=2
5. WHILE (x<10) AND (y<10)
6. WriteF('x is \\d and y is \\d\\n', x, y)
7. x:=x+2
8. y:=y+2
9. ENDWHILE
10. ENDPROC
Hopefully, you should be able to recognise all the features listed in the
table below. If you don't then you might need to go back over the
previous chapters, or find a much better programming guide than this!
@{i }Line(s)@{ui } @{i }Observation@{ui }
---------------------------------------------------------
1-10 The procedure definition.
1 The declaration of the procedure @{b }main@{ub }, with no
parameters.
2 The declaration of local variables @{b }x@{ub } and @{b }y@{ub }.
3, 4 Initialisation of @{b }x@{ub } and @{b }y@{ub } using assignment
statements.
5-9 The @{b }WHILE@{ub } loop.
5 The loop check for the @{b }WHILE@{ub } loop using the
logical operator @{b }AND@{ub }, the comparison operator
@{b }<@{ub }, and parentheses to group the expression.
6 The call to the (built-in) procedure @{b }WriteF@{ub }
using parameters. Notice the string, the place
holders for numbers, @{b }\\d@{ub }, and the linefeed,
@{b }\\n@{ub }.
7, 8 Assignments to @{b }x@{ub } and @{b }y@{ub }, adding two to
their values.
9 The marker for the end of the @{b }WHILE@{ub } loop.
10 The marker for the end of the procedure.
@ENDNODE