Before any understanding of Euphoria programming is begun, you must first understand what the terms"program" and "programming" mean. A program, simply put, is a series of computer instructions organized in order to complete a given task. For example, this tutorial's task is to teach a newcomer how to program in Euphoria.
It is made up of computer instructions organized in a way so it can carry out that given task. More complex programs may have their primary task broken down into a series of smaller tasks. For example, a spreadsheet program's primary purpose is to organize and calculate numeric data but this is then broken down further into smaller tasks like graphing forecasting, incorporating the computed figures into a report and so
forth. Your very first programs will have very simple tasks, but as you create larger programs, those tasks will become more complex.
To create a program on your computer is referred to as "programming".
You may have also heard of the phrase "coding", which is the same
as "programming". "Coding" comes from the fact that the
instructions that make up a program are sometimes referred to as
"program code". It doesn't really matter what terms you use, just
as long as you understand what they mean. But exactly what do the
individual instructions that make up a program look like? And how
do we enter and group program instructions to create a
program? Just as all words of our vocabulary make up the
English language, a set of all related computer instructions make
up a programming language. The one programming language a computer
understands is called "binary". It's called binary because the
program instructions are made up of two numbers, 1 and 0, like
100101 for example. Binary language is also referred to as
"machine language".
Unfortunately, to write a program in this very difficult language
is beyond the ability of most people, save for a very chosen few.
The very first computers of the 1950's and 1960's could only be
maintained by scientists who had a good understanding of machine
language. As time went on, more understandable and easy to use
programming languages were created. The program instructions of
these languages were more English-like in appearance. Perhaps you
may have seen or heard of some of them, such as BASIC, COBOL, RPG,
ADA, FORTRAN, PASCAL, C and others. Euphoria is the newest form of
computer programming language. For your personal interest,
Euphoria stands for End User Programming with Hierarchical Objects for Robust Interpreted Applications.
Quite a mouthful, but as you will soon discover, the name is the
only difficult part of the Euphoria programming language to
understand.
To create a Euphoria program, you first start up an editor program
that lets you enter Euphoria programming instructions, called
"statements". Just as if you were writing a letter, you start
typing at the top of the editor window, and then proceed
downwards. Exactly what you will be typing will be revealed soon.
Understand for now you will require an editor program to do this.
MS-DOS's EDIT program is satisfactory, or you can use ED, which
comes with the Euphoria software. Once you have finished typing
your Euphoria program, you can save it to the hard drive or floppy
drive as a file with .EX as the extension. But you're not finished
just yet. Remember that your computer only understands machine
language.
It cannot understand Euphoria programming statements.
So, it must be converted to machine language.
There are two types of programs that can translate Euphoria to
machine language. Both do the same thing but they differ in their
methods. One is called a compiler. It takes a Euphoria program and
creates a machine language program. This machine language replica
has the same name as your Euphoria program, but has a different
file extension of .EXE. The other type of translator program is
called an interpreter. It translates each Euphoria program
statement to a machine language instruction, which is then run.
This differs from a compiler, which creates the entire program to
be run. Euphoria both comes with a compiler (BIND.BAT) and an
interpreter (EX.EXE). To run a program using EX.EXE, you type:
EX filename.EX
To create an executable using BIND.BAT, you type:
BIND filename.EX
When the executable is created, type:
filename
The Euphoria program that BIND.BAT will translate into
a machine language program is called a "source file". The machine
language program is called an "object file". EX.EXE generates "object
code". Sometimes it may not be possible to successfully translate
your Euphoria program to machine language. While entering Euphoria
statements, it is possible you may misspell a word. Just as we
have spelling and grammar rules in the written language, Euphoria
also demands we follow set rules when typing in these statements.
When a spelling or grammar type error is made during the typing in
of these statements, it's referred to as a "syntax error". When
your compiler encounters this error in your program while trying
to translate it to machine language, it will stop. You will then
see a message explaining what the error is, and where it is in
your Euphoria program. You then start up your editor, correct the
statement in error and try to run the compiler or interpreter
again.
All programs, no matter what purpose each serves, perform one
function. They all process data. This processing of data is broken
down into the following three stages listed below:
- A program will accept data. The data
will consist of numeric figures from either an external source (a
keyboard, mouse, digital camera,or voice card) or from somewhere inside
the computer (such as a file on
a CD-ROM, floppy diskette, or your hard drive).
- A program will analyze the accepted
data. This step involves temporarily storing the data for both
arithmetic calculations and comparison against predefined values, and
then making a decision based on the result of the calculation or
comparison.
- A program will present data in a
meaningful form. This means either displaying figures on the screen or
printer, storing information on the hard or floppy drive for another
program to use later, or creating
graphics and sound from the computer that has a meaning to the person running the program.
No doubt all of this has you eager to start learning how to write
your own Euphoria programs. Well, let's get started on learning
the actual concepts and instruction statements of the Euphoria
programming language!
ToC
Variables And Data Objects
We mentioned previously that a program will store data for
analysis. Your computer's memory (RAM) best resembles a huge group
of mail boxes, each of which is uniquely addressed by a number.
Each of these storage locations can hold a value between 0
and 255. Each of these values is referred to as a "byte". If
values over 255 need to be stored in RAM, it is split up between
various locations. While data is always stored as numbers in
RAM, it can be both numeric or characters like "A". To write
a program that accesses RAM locations by address number would be
tedious, especially when dealing with large data
values. Thankfully, Euphoria offers a way to access stored
data in RAM not by the actual RAM address number, but by a label
like "salary" or "points". This symbolically referenced memory
location is called a "variable".
Variables are invaluable for two reasons. First of all, it's much
easier to know where all your data is located in your program if
you use meaningful names. For example, if you are writing a space
combat game and want to store data representing the amount of fuel
you have left in your ship, storing it in a variable called "fuel"
makes it so much easier to find than some obscure RAM memory
address like 32767. In addition, because a variable holds a single
stored value regardless of its size, there's no complex handling
of multiple RAM locations when dealing with very large values.
Euphoria does this for you behind the scenes. When it comes time
to compile or interpret your program to be run, variable names are
converted automatically to actual RAM memory addresses. But that
is something a Euphoria programmer does not need to be concerned
about.
Variable names can be any length in size, and can both be real
words or made up ones that border on nonsense, as long as the name
itself is meaningful to the programmer. However, Euphoria does
place some limits on what you can use for a variable name. First
of all, variable names must start with a letter and then can be
followed by any combination of letters, numbers and the underscore
("_"). Second, case is significant. This means the variable name
"tax" is not the same as "TAX". Finally, words used in the
Euphoria language cannot be used as a variable name. They include,
but are not limited to, words like "and", "global", "function",
"while", and "exit". These words are called "reserved words". A
complete list can be found in the Euphoria Reference Manual.
Now that we have completed our understanding of variables, let's move
on to learn about the type of data we can store inside a variable.
In Euphoria, all data is referred to as "data objects". The reason this
term is used to describe data is because data isn't something you
work out in arithmetic calculations. Instead, data in Euphoria
is viewed as tangible items you can merge together, break apart,
twist, or alter at the slightest whim. As we go further into
understanding the Euphoria language, you will soon see this to be
true. Data objects come in two types. The first type is the atom,
which is a single numeric value. Below are examples of atoms:
2001 12.4 -5
3.14e3
The first three examples are very familiar to all of us, but the
fourth is an example of Standard Notation. The "e" means "times 10
to the power of", with the number following. This means 3.14e3 is
really 3.14 times 10 to the power of 3, which works out to 3140.
Standard notation is best used to represent atom values in a
compact form. Atoms can either be a floating point (with a decimal
point) or integer value (no decimal point) value, and can be
either positive or negative. Atoms can have a value range of
approximately between -1e300 and +1e300 (that's -1 followed by 300
zeros to 1 followd by 300 zeros, inclusive). While chances are
good you will never design a program that handles such huge
numbers, it is nice to have that wide a margin to work with.
The second type of data object is called a sequence, and is a
little more complex in structure. Sequences are a list of data
objects joined in the same manner as links on a chain. Each linked
data object is referred to as an "element". Sequences can be
made up of either atoms, smaller sequences, or any mixing of both.
You can have sequences inside of sequences, which in turn are part
of bigger sequences, and so on, to any level of dimension.
Computer memory is the only limiting factor.
Sequences always start with a "{", have commas or ","
separating each individual data object, and a closing "}". Here
are some examples of sequences:
A sequence made up of atoms: {2,4,6,8,10}
A sequence that is made up of three smaller sequences: {{31,32,33,34,35},{41,42,43},{51,52,53,54,55,56}}
namely {31,32,33,34,35}, {41,42,43} and {51,52,53,54,55,56}.
A sequence that is made up of both an atom and two sequences: {{100,101,102},200,{301,302,{-401,-402},303}}
namely {100,101,102},200 and {301,302,{-401,-402},303}.
Notice that the third sequence example has a sequence within a
sequence as the third element. Sequences can be represented
in the form of a character string, like the text you are reading
now. Character strings begin with a quotation mark followed by any
numbers, letters or special characters, then ended with a second
quotation mark. The character string is translated by Euphoria to
the sequence's real form automatically. For example, the following
two values:
"David Alan Gay"
{68,97,118,105,100,32,65,108,97,110,32,71,97,121}
are identical in value. They only differ in the way they are
presented. The numbers in the second value are the ASCII codes of
each character. ASCII is a convention that assigns each character,
displayable or not, a numeric code. This convention was created to
ensure that all computers, no matter who made them, will display
data the same way. ASCII stands for
American
Standard
Code for
Information
Interchange.
Character strings are best used to define sequences that are to be
used for display on a screen or printer, such as names, addresses,
etc. If you are feeling a little overwhelmed by all these terms,
like "variables", "data objects", "atoms", and "sequences",
don't be alarmed. It's because you haven't had the chance to see
how they work in Euphoria. That will now change. You will now be
introduced to your first Euphoria programming statements, by
learning how to create variables for your program to use later.
ToC
Declaring Variables In Euphoria
Before using variables in Euphoria, you first must "declare" them.
Declaring a variable is similiar to declaring items in front of a
customs officer. When you declare items, you tell the officer
what they are, what type of items they are and so forth. In Euphoria,
declaring variables in a program involves two things: stating what
they are going to be named, and defining the type of
data they are supposed to hold. To declare a variable in
Euphoria, you use the following syntax:
variable type variable name
variable type means the type of data object the variable will hold.
variable name means the name of the variable, of course.
The first part of the variable declaration, the variable type, comes in
only four accepted words: "sequence", "atom", "object",
and "integer". "sequence" means the variable can only hold data
objects that are sequences. You cannot place atom data objects in
this type of variable. "atom" means the variable can only hold
data objects that are atoms. Sequences are not allowed. A variable
type of "object" means the variable can hold both atom and
sequence data objects. One must wonder why we bother having
variables of type atom and type sequence when a type object
variable can hold both. Type object variables are needed to
hold the result of program data processing where the data type is
unknown. "integer" means the variable can hold atoms, but only
integer atoms. An integer atom can have a value between
-1073741824 and 1073741823. If you want to use even larger integer
in your programs, you need to use the type atom variables to hold
them.
Variable declarations usually appear at the top of the program, so
they are one of the first things typed in by the programmer.
However, there are exceptions where they may appear elsewhere in
the program. A variable declaration is entered only once for every
variable in the program. Once entered, you cannot enter a second
variable declaration using the same variable name, even if the
variable type is different. Let's start entering our very first
Euphoria statements. To declare a variable named "address" that
holds sequence type data objects, we enter:
sequence address
To declare a variable named "age" that holds atom data objects we enter:
atom age
To declare a variable named "grab_bag" that can hold both atoms and sequences, we enter:
object grab_bag
to declare a variable named "Whole_Numbers" that can hold integer atoms between -1073741824 and 1073741823, we enter:
integer whole_numbers
When dealing many variables of the same type, you can declare them
all with one variable type followed by a series of variable
names. For example:
sequence name, address, city, country
will just as easily declare these four variables as if you used a
single declaration line for each variable. If you wanted to get
really fancy, this is also a legal way to declare multiple
variables:
atom hours_worked,
hourly_rate,
deductions,
net_income
Euphoria offers the ability to split variable declarations (and
other types of statements) into several lines as opposed to one
line. Just remember the commas, and only split the line where
there is a space. Having a variable type as part of a variable
declaration is a kind of safety system. It prevents a programmer
from entering the wrong type of value into a variable. It also
ensures that certain features of the Euphoria programming language
that are meant to work on one data
type do not get slipped with a variable value that is of a different
data object type. Which brings us next to the topic of
variable values. Is there an initial value placed inside a
variable after it is declared? The answer is no. Don't get the
impression, however, that there is nothing inside a variable. Most
likely it is left-over data from a previous program run, and probably
so garbled that it is unusable. For this reason, Euphoria
rules dictate that a programmer must place a value
inside a variable for the first time before it can be used. The
next few chapters will discuss the process of
initializing variables with values, and even changing those
values. This is an important part of Euphoria that must be clearly
understood, because it involves the primary purpose of all
programs, which is to process data.
ToC
Assigning Values To Atom Variables, Part One
This chapter begins the topic of placing values in atom variables.
But first, an important note. Even though this chapter uses atom
variables in the examples, what you learn here also pertains
to object type variables. You can also use integer variables,
but remember that integer variables only hold positive and
negative whole numbers. To place values in atom (and in other
types of) variables, you use an "assignment statement". The
concept of assignment statements is really quite easy to
follow. Here is the syntax of the assignment statement listed
below:
variable = expression
variable is of course, a variable. The equal sign means "is given the value of".
expression is either a constant value, another variable, or a complex
formula. An expression is evaluated to a single value, which is
then stored in the variable to the left of the equal sign. Let's
begin with a simple example of assigning a variable with a value:
atom year, copy_of_year
year = 1997
copy_of_year = year
(Author's note: first, you could say the above is a simple
example of a program, and second, from this time forth, any
examples involving Euphoria will always show the appropriate
variables declared.) In the example on the previous page, the atom
variable "year" is being assigned a value of 1997. The value 1997
is the simplest example of an expression, as it is already a value
of 1997. The variable "copy_of_year" is given a value of what was
just placed in "year". Euphoria checks to see what is inside
"year", and then places that value in variable "copy_of_year",
which is 1997.
atom letter_of_the_alphabet
letter_of_the_alphabet = 'C'
In the example above, the variable, "letter_of_the_alphabet" is
being assigned a numeric value, but the numeric value is
represented here as a character between single quotes or '. This
character representation is evaluated into the numeric ASCII value
of the letter C. This means "letter_of_the_alphabet" ends up
containing a value of 67. Here's another way to assign an atom variable
with a numeric value:
atom double_quotation_mark
double_quotation_mark = '\"'
This example program places the ASCII value of a double quotation
mark in variable "double_quotation_mark", which is 34. It works
just like the previous example that used a character
representation of a numeric value. This form of expression is
commonly used for special characters that cannot be entered by
keyboard, or to use Euphoria symbols like the for output. Other
examples of \-prefixed special characters include \n for new line,
\t for tab, and \\ for reverse slash. A complete list of these
special characters is explained in the Euphoria Reference Manual.
But expressions can also be more than just a special representation
of a single value. They can be the result of very complex
arithmetic calculations. In this case, expressions are a
combination of numbers, other variables, and special arithmetic
operators. In Euphoria, the following symbols listed below are
arithmetic operators:
+ addition
- subtraction
* multiplication
/ division
Let's put these operators to use in the program example on
the following page. Here is a program example that works out the
amount of tax paid and the
total cost of a pair of jeans.
atom jeans, tax, tax_paid, total_cost
jeans = 22.00
tax = .07
tax_paid = jeans * tax
total_cost = jeans + tax_paid
Variable "jeans" represents the sale price of a pair of jeans, so
we assign it a value of 22.00 (with decimal points to look like
dollars). variable "tax" is then assigned a value of 7%, which is
0.07. Next, variable "tax_paid" is computed by multiplying the
values of variables "jeans" and "tax" together. Finally, variable
"total_cost" is assigned the sum of the values stored in
"tax_paid" and "jeans". The previous program examples have all shown
variables being initialized for the first time, and only once.
What happens if a variable already contained a value, but was
assigned a new value? Well, the old value of the variable would be
replaced with the new value. A variable may only hold one value at
any time. But this shouldn't be considered a problem, as variable
value modification is just as important as initialization. Note
the following example:
atom counter
counter = 0
counter = counter + 1
counter = counter + 1
counter = counter + 1
What is the value of variable "counter" after this program
finishes? Let's look at the program again, this time examining it
line by line:
atom counter
counter = 0 -- "counter" is initialized to 0
counter = counter + 1 -- "counter"'s value is now 1, by adding 1 to 0
counter = counter + 1 -- "counter"'s value is now 2, by adding 1 to 1
counter = counter + 1 -- "counter"'s value is now 3, by adding 1 to 2
After variable "counter" is set to zero, its old value is
replaced with a new one in the next 3 lines, by taking the
original value and adding 1 to it to produce a new value. This is
done three times in the program example. An introduction to
relational and logical expressions is our next stop!
ToC
Assigning Values To Atom Variables, Part Two
Continuing our discussion of assigning atom variables with values,
we now introduce relational and logical
expressions. Relational means based on comparison. For example,
you can be taller than one person, yet shorter than another. Your
parents are older than you, yet at the same time, you are older
than your children or younger siblings. It works the same way
in Euphoria. Is one variable value larger or smaller than an
expected value? Is this value equal or unequal in a comparison
with another value? Relational expressions are evaluated to one of
two values: 1 for true, or 0 for false. Here are a list of valid
Euphoria relational operators:
< less than
> greater
than
<= less than or equal to
>= greater than or equal to
= equal to
!= not equal to
Here is a program example that uses several of these operators in
relational expressions. See if you can guess the value of each
variable first before reading the explanation of each line:
atom t1, t2, t3, t4
t1 = 5 > 4 --1 (TRUE) in "t1": 5 is GREATER than 4
t2 = 7 != 7 --0 (FALSE) in "t2": 7 is EQUAL to 7, not UNEQUAL
t3 = 13 <= 13 --1 (TRUE) in "t3": 13 is LESS THAN OR EQUAL to 13
t4 = 12 = 2 * 6 --1 (TRUE) in "t4": 12 is EQUAL to 2 * 6, which is 12
Logical expressions focus more on whether a given situation is true
or false. For example, if one says, "The bedroom light is switched
on", the statement is either true (the light is indeed switched
on), or false (the light is not switched on). The analysis of
the truth of a situation can be more complex than the bedroom
light example. For instance, the handling of delinquent
accounts at a loans company requires more than one condition to be
met. First of all, the account must be in a state of not being
paid. Second, it must be in that state for a given set of time
before the customer is called. Both of these conditions must be
true before an account is considered delinquent (the given
situation is true). If any one of the conditions are not met, then
the account cannot be delinquent (the given situation
is false). Euphoria has logical operators that work using a system
called BOOLEAN LOGIC. This system dictates that the outcome of
paired conditions, either
single values or relational expressions, are based on the rules below:
| Condition 1 |
|
Condition 2 |
Result |
| 1 (true) |
and |
1 (true) |
1 (true) |
| 0 (false) |
and |
1 (true) |
0 (false) |
| 1 (true) |
and |
0 (false) |
0 (false) |
| 0 (false) |
and |
0 (false) |
0 (false) |
| 1 (true) |
or |
1 (true) |
1 (true) |
| 0 (false) |
or |
1 (true) |
1 (true) |
| 1 (true) |
or |
0 (false) |
1 (true) |
| 0 (false) |
or |
0 (false) |
0 (false) |
An "and" logical expression
only evaluates to a single true value if both conditions are
themselves evaluated to be true, and an "or" logical expression is
only false when both conditions are false. So let's put this
knowledge to use in an example Euphoria program:
atom value1,value2,test1,test2,test3,test4
value1 = 50
value2 = 25
test1 = value1 < 10 and value2 = 25
test2 = value1 > 5 and value2 = 12.5 * 2
test3 = value1 != 100 or value2 < 90
test4 = value1 = 5 or value2 > 27
Try to guess the value of each variable before going to the next page.
value1 = 50
value2 = 25
test1 = value1 < 10 and value2 = 25
Because variable "value1" is not smaller than 10, the first relational expression is false. Variable "value2" is equal to 25, so the second part of the "and" expression is true. But because one of the relational expressions were not true, "test1" is assigned a value of 0.
test2 = value1 > 5 and value2 = 12.5 * 2
Because variable "value1" is larger than 5, the first relational expression is true. Variable "value2" is equal to 12.5 * 2, so the second relational expression of the "and" logical expression is true. Because both expressions are true, "test2" is assigned a value of 1.
test3 = value1 != 100 or value2 > 2000
The first relational expression of this "or" logical
expression works out to be true because 50 is not equal to 100.
The second relational expression works out to be false because 25
is not larger than 2000. As a result, the "or" logical expression
works out to be true because at least one of the expressions was
true, so variable "test3" is assigned a value of 1.
test4 = value1 = 5 or value2 < 49
Variable "value1" is certainly not equal to 5, so the first
relational expression is false. Variable "value2" is not smaller
than 49, so the second relational expression is also false.
Because neither relational expression worked out to a true value,
the "or" logical expression is false. This means "test4" is
assigned a value of 0.
Euphoria also has a "not" logical operator that gives you the
opposite value of a relational expression. Here's a program
example that best demonstrates this:
atom opposite, result
result = 15 < 20
opposite = not result
First, "15 < 20" is evaluated, which has 1 (true) stored in
variable "result", Next, "not" reverses the value in "result" to 0
(false), so the variable "opposite" is assigned a value of 0. In
short, if the expression beside a "not" works out to be false,
"not" reverses it to true. If the expression works out to be true,
"not" reverses it to false."not" gives you the opposite value of the
outcome of an expression. You can link as many relational and
logical expressions together to make
larger complex expressions. You can even link arithmetic, relational
and logical expressions together to formulate a value to be stored
in the appropriate variable. However, you should know a few
important things. In a situation where you are using the equal
sign (=) in a relational expression, Euphoria uses a rule to
define when the equal sign is an assignment operator and when it
is a relational operator. The leftmost equal sign on the line is
assumed to be an assignment operator. All other equal signs that
follow are assumed to be relational operators. The outcome of
arithmetic expressions can be tested with logical operators. This
means you can do expressions like the following:
complex_result = value1 * 5 and value2 - 8
If arithmetic expressions are used with logical operators,
non-zero results like 5.2 or -4 are assumed true. Zero results
still mean false. Euphoria also uses a system called precedence
that defines which parts of larger complex expressions are done
first by default. For example:
atom rent1, rent2, rent3, average_rent
rent1 = 500
rent2 = 600
rent3 = 400
average_rent = rent1 + rent2 + rent3 / 3
This example is supposed to work out the average of three rents.
But precedence dictates that division is done first before
addition. This means this program will work out the average of the
rents incorrectly! We can fix this by using parentheses to force
different parts of the expression to be worked out in a manner
different from precedence of operators:
average_rent = (rent1 + rent2 + rent3) / 3
This forces the three rents to be added together before division
takes place. A complete list of precedence operators is in the
Euphoria Reference Manual.
This completes your introduction to assigning atom (and object)
type variables. If you clearly understand variable assignment,
arithmetic, logical and relational expressions, and precedence,
move on to sequence variable assignment in the next chapter. If
not, please go over the two chapters again . You must understand
these concepts before moving on to sequence variable assignment.
ToC
Assigning Values To Sequence Variables
Assigning values to sequence variables is just like
assigning values to atom variables. Sequence variables can be
given an actual value, or the result of an expression. And as
with atom variables, the expressions can be arithmetic, relational
or logical. However, because sequences consist of linked data
objects instead of a single one, there are a few twists to learn
about. It's important to note that the examples used in this
chapter can also be applied to object type
variables. Here's a simple example of a sequence variable
being assigned a value:
sequence list_of_values
list_of_values = {1,2,3,4,5,6,7,8,9,0}
It is also legal to assign a sequence variable the following value:
sequence Mister_No_Elements
Mister_No_Elements = {}
This is an example of a sequence value with no elements. You would
use this approach to assign a sequence variable an "empty" value
before using it in the program. It's similiar to setting an atom
variable with a zero value. If you wanted to store a sequence
value that was a person's name, address or city of residence, you
could use the previously mentioned character string method to
represent a sequence value.
sequence my_name
my_name = "David Alan Gay"
In this program example, we're storing "David Alan Gay" into a
sequence variable meant to be my name. But understand that
Euphoria converts this character string into the actual sequence
value before it is stored in variable "my_name". If you could look
inside variable "my_name", this is what you would see:
{68,97,118,105,100,32,65,108,97,110,32,71,97,121}
It's important to note that a character string of "D" for example
is NOT the same as 'D'. The former is a character string
representation of a sequence value that is one element long, while
the latter is a character representation of an atom value. Having
said this, you shouldn't assume also that you can assign a
one element long sequence value into both an atom variable and a
sequence variable. An atom value and a one element long sequence
value are NOT identical! The previous sequence variable
assignment examples used values that are considered
one-dimensional (where the elements are all atom values). Remember
that sequences can also be composed of smaller sequences or a mix
of atoms and smaller sequences. This program example assigns
more complex sequence values to a series of sequence variables:
sequence seq1, seq2
seq1 = {{1,2,3}, {4,5,6}, {7,8,9}}
seq2 = {{5,5,5,5,5,5,5}, -98, {100,-50,20.3}}
You could also represent the
previous program example to help clarify the individual elements
of each sequence using split lines:
sequence seq1, seq2
seq1 = {{1,2,3},
{4,5,6},
{7,8,9}}
seq2 = {{5,5,5,5,5,5,5},
-98,
{100,-50,20.3}}
Instead of using constant
elements in a sequence value, you are also allowed to use variable
names and expressions to define the elements in a sequence value
to be stored in a sequence variable:
sequence mixed_bunch
atom some_atom_element
some_atom_element = 502
mixed_bunch = {"Euphoria", some_atom_element, some_atom_element/2}
Before this sequence value is stored in variable "mixed_bunch",
each individual element must be worked out. In element 1, the
character string "Euphoria" is converted to a true sequence value.
In element 2, the value of "some_atom_element" is used. In element
3, the expression of the value of "some_atom_element" being
divided by 2 is worked out. Once these steps are completed, the
actual value is stored in "mixed_bunch". You can also use
arithmetic, relational, and logical expressions to assign values
to sequence variables, using the same operators introduced in the
previous chapters on atom variable value assignment. This
program example below works out a rent increase for five different
apartments:
sequence old_rents, new_rents
atom rent_increase
old_rents = {413,500,435,619,372}
rent_increase = 1.05
new_rents = old_rents * rent_increase
Variable "old_rents" is given a 5-element sequence value that is
the old rent amounts for five apartments. "rent_increase" is
assigned the percentage to raise the rents by. "new_rents" is the
new rents of the five apartments. Let's look at the statement that
works out the new rents more closely:
new_rents = old_rents * rent_increase
When using both atom and sequence variables or values in a
binary expression (something that is made up of two parts), the
atom part is replaced with a temporary sequence value that is as
long as the other sequence. This is done before the expression is
evaluated. As a result, variable "rent_increase"'s value becomes
{1.05,1.05,1.05,1.05,1.05}. So, the above expression is then
worked out in the following manner:
element 1 of "old_rents" (413) × element 1 of temporary sequence (1.05)
element 2 of "old_rents" (500) × element 2 of temporary sequence (1.05)
element 3 of "old_rents" (435) × element 3 of temporary sequence (1.05)
element 4 of "old_rents" (619) × element 4 of temporary sequence (1.05)
element 5 of "old_rents" (372) × element 5 of temporary sequence (1.05)
The result of this element by element multiplication stores the value of:
{433.65, 525, 456.75, 649.95, 390}
in variable "new_rents". This also works for logical and
relational expressions as well. Here is a program example that
uses sequence and atom in both expression types:
sequence test1,test2
test1 = {1,0,0} and 0
test2 = {20,30,40} <= 30
Try to determine the value of "test1" and "test2" before going on
to the next page. It's just like the rent increase program example!
element 1 of {1,0,0} (1) and element 1 of {0,0,0} (0) = 0
element 2 of {1,0,0} (0) and element 2 of {0,0,0} (0) = 0
element 3 of {1,0,0} (0) and element 3 of {0,0,0} (0) = 0
Thus, variable "test1" is assigned a value of {0,0,0}
element 1 of {20,30,40} (20) ≤ element 1 of {30,30,30} (30), giving a value of 1.
element 2 of {20,30,40} (30) ≤ element 2 of {30,30,30} (30), giving a value of 1.
element 3 of {20,30,40} (40) ≥ element 3 of {30,30,30} (30), giving a value of 0.
Thus, variable "test2" is assigned a value of {1,1,0}
If two sequence variables or values are involved in a binary
expression, both sequence lengths must be the same. For example,
you cannot have an expression that tries to add a five element
sequence and a six element sequence together to produce a result.
You can, however, use sequences that are the same length but with
different element types. This means you can add a sequence
composed of atoms to a sequence composed of sequences. This
program example helps clarify this point:
sequence seq1, seq2
seq1 = {1,0,1,0,1} or {0,1,0,1,0}
seq2 = {2,{2,2},2} + {{2,2},2,{2,2}}
Variable "seq1" is assigned a value of {1,1,1,1,1}, while "seq2"
is assigned a value of {{4,4},{4,4},{4,4}}. The rule of atoms and
sequences in a binary expression was used in working out "seq2"'s
value.
This chapter not only taught you how to assign values to sequence
type variables, it also showed you how powerful sequences can be in
data manipulation. With a single Euphoria statement, you can
change a series of linked values in a flash. Try doing that with a
handful of atom variables! But all of this is at the top
level of the sequence. How do we access and change individual
elements of a sequence, or create new sequence values by joining
existing atom and sequence values in our programs? Well, the next
chapter will focus on these topics as we journey further in
the adventure that is the Euphoria language!
ToC
Sequence Element Access And Manipulation
Even though we devoted an entire chapter of this tutorial to the
assignment of sequence variables with entire sequence values, it
is actually the handling of elements that will take up most of
your time when programming with sequences. This makes sense,
as sequences allow the programmer to handle many individual data
objects very quickly and all at once. This chapter will show you
how to extract elements out of sequences and also to
link them together to make new sequences. To start off, you need to
know the Euphoria syntax required to reference individual elements
of a sequence. It is shown on the next page, as it
requires a considerable amount of explanation.
variable = sequence variable[element number(s)]
Accessing an element involves the use of an assignment statement. sequence variable
contains the value that we want to reference the elements
in element number(s) means
one or more element numbers that are being referenced. The square
braces [ and ] are used to tell Euphoria that this is an
element number and are always on either side of the number. The
first element number in a sequence always starts at 1, and
increases by one with the second and following elements
after. Accessed elements of a sequence are stored in a receiving
variable, defined here to the left of the equal sign as variable. The actual
type
of the variable to receive the element depends on the type of
data object the element is. If the element is a sequence, the
receiving variable must be able to hold sequences. If the element is an
atom, the receiving variable must be able to hold atoms.
sequence list_of_days, list_of_months, month_name
atom days_in_month
list_of_months = {"January","February","March","April","May","June",
"July","August","September","October","November","December"}
list_of_days = {31,28,31,30,31,30,31,31,30,31,30,31}
days_in_month = list_of_days[3]
month_name = list_of_months[3]
This program example obtains the name and number of days for the
month March from two sequence variables ("list_of_months" and
"list_of_days") that were previously assigned data. "month_name"
is given the value of the third element of "list_of_months", and
"days_of_month" is given the third element of list_of_days".
Notice both receiving variables have the correct variable type
needed to accept the values. This is important! When accessing an
element of a sequence that is itself a sequence, it is possible to
access the elements inside that sequence as well. What is required
is another element index to access a second level. To
access elements within a two-dimensional index, you use the syntax
below:
variable = sequence variable [element number(s)][element number(s)]
If we were dealing with sequence made up of sequences, the first
element number(s) serves as the index number needed to access any of
the sequences making up the main sequence. The second element
number(s) serves to access the elements within the sequence
referenced by the first element number(s). A program example on
the next page will help clear up any confusion in understanding
how to use two indexes to reference elements.
sequence days_of_months
atom no_leap_february, leap_february
days_of_months = {31,{28,29},31,30,31,30,31,31,30,31,30,31}
no_leap_february = days_of_months[2][1]
leap_february = days_of_months[2][2]
This program example extracts the number of days in February in both
a leap year and non-leap year situation. Look at the second
element of the sequence value stored in variable "days_of_months".
It is itself a sequence two elements long. The atom variable
"no_leap_february" is assigned the value of 28, the first element
that makes up the sequence that is the second element of
"days_of_months" ([2][1]), while atom variable "leap_february" is
assigned 29, the second element that makes up the second element
of "days_of_months" ([2][2]). Notice the indexes are in order of
descending level, giving it a reversed appearance.
Here are the "dos" and "don'ts" of using element indexing in
any sequence. First of all, you can replace a constant number with
a variable name, like "[element_id]" for example. You can
also have more than two levels of element access if the sequence
is built for it. However, if an element is an atom, you cannot use
another level of element indexing to go deeper into that element:
only elements that are sequences can be accessed in this manner.
Also, you cannot use an element number of zero, or an element
number that is larger than the length of the sequence
(for example, trying to access element 4 in a sequence that is
only 3 elements long). It's possible to reference more than
one element of a sequence at a time. The syntax below is a
variation of what you were introduced to before:
seq. variable = seq. variable[starting element..ending element]
Euphoria uses double periods
(..) to indicate that this is not a single element we are
accessing but an inclusive (meaning including the starting and
ending points) range of elements. The program example below shows
how to access a range of elements:
sequence nine_numbers, first_four, last_five
nine_numbers = {1,2,3,4,5,6,7,8,9}
first_four = nine_numbers[1..4]
last_five = nine_numbers[5..9]
"first_four" contains {1,2,3,4}
"last_five" contains {5,6,7,8,9}.
A receiving variable is always of type sequence, as using a range
returns a sequence value, even if the starting and ending elements
are the same. Accessing ranges follow the same rules as accessing individual
elements. In addition, the starting and ending element numbers
must be within the length of the accessed sequence. If the
starting element number is equal to the ending element number + 1,
a null sequence ({}) is returned. You cannot, however, have a
situation where the starting element number is greater than the
ending element number by more than 1. You can use ranges when
dealing with multi-dimensional sequences, but there are
some limitations as listed below in this example:
sequence bigseq, seq1, seq2
bigseq = {{1,1,1},{2,2,2},{3,3,3}}
seq1 = bigseq[1][1..2]
seq2 = bigseq[1..2][1]
While you can access a range in a sequence element, you cannot reverse
it to access an element out of a range of sequence elements, as in
"seq2". If we changed the syntax around to place the referenced
sequence element on the left side of the equal sign, we would have
the ability to change the value of a specific element:
sequence variable [element number(s)] = expression
Here is a program example that demonstrates how to change both a single element and a range of elements:
sequence bunch
bunch = {"cat",5,{1,9,8,4},{0,0,0}}
bunch[1][1] = 'b'
bunch[2] = {7,7,7} + 1
bunch[3][3..4] = {9,7}
bunch[4][1..3] = -20
Let's walk through the program example to understand what is going on:
bunch = {"cat",5,{1,9,8,4},{0,0,0}} - assign variable "bunch" with the value of {{99,97,116},5,{1,9,8,4},{0,0,0}}
bunch[1][1] = 'b' - access first element in element 1 of "bunch" and change it from 99 ('c') to (98) ('b'), "bunch[1]" is now {98,97,116}
bunch[2] = {7,7,7} + 1 - access second element of "bunch" and change it to the value of expression {7,7,7} + 1, "bunch[2]" is now {8,8,8}
bunch[3][3..4] = {9,7} - access third and fourth elements in element 3 of "bunch", and replace with {9,7}, "bunch[3]" is now {1,9,9,7}
bunch[4][1..3] = -20 - access all three elements in element 4 of "bunch" and replace with {-20,-20,-20}, "bunch[4]" is now {-20,-20,-20}
When a single atom value is being assigned to a range of
elements, Euphoria converts this to a temporary sequence value
equal to the length needed to cover the starting and ending
element numbers in the range. The sequence value is made up of the
original atom value repeated as many times as needed. That is why
"bunch[4]" is assigned a value of {-20,-20,-20}. When the
program example completes, the value of variable "bunch" is:
{{98,97,116},{8,8,8},{1,9,9,7},{-20,-20,-20}}
You can also create new sequence values in variables by adding
together existing atom or even sequences. To do this, you use the
syntax below:
sequence variable = expression & expression
The & operator joins
together expressions that evaluate into atom or sequence values to
form new sequences. The result is a sequence that is as long as
(in elements) the total sum of the lengths of the atoms
and sequences used in the joining. For example, joining a four
element long sequence with an atom gives a five element long
sequence. This program example demonstates the results of several
joining attempts:
sequence s1, s2, s3
s1 = 5 & 4 -- s1 is assigned {5,4}
s2 = 90 & {30,60} -- s2 is assigned {90,30,60}
s3 = {{1,1},{2,2,2}} & {3,3,3} + 1 -- s3 is assigned {{1,1},{2,2,2},4,4,4}
Congratulations! You now understand the basic concepts of Euphoria!
Feel free to review the previous chapters again. A full
understanding of the core concepts is needed to learn the topics
in the next chapters ahead.
ToC
Introduction To Library Routines
The chapters you have read so far have helped teach you how to
accomplish the primary purpose of a program: the processing of
data. You know the two types of data, you know how to
declare variables to hold data, and you know how to
use assignment statements in order to initialize and change
values in variables. However, this is only the core. The
declaration and assignment statements alone are not enough to make
a program that actually does something useful. Euphoria has
something called "library routines" that allow you to do things
beyond the power of simple assignment statements. When requested
by the programs that you will write, they allow you to
access specific features of your computer. This allows you to
write software like games, office and home applications, or system
utilties. Euphoria (Version 1.5) has grouped all library routines into
the following categories:
- Predefined Types - library routines that test the type of a data
- Sequence Manipulation - library routines that offer advanced sequence handling features
- Searching and Sorting - library routines that compare, look for, and sort data objects
- Pattern Matching - library routines that can convert alphabet case and allow pattern matching in a string of characters
- Math - library routines that handle advanced mathematic formulas far beyond the power of the math operators +,-,/, and *
- Bitwise Logical Operators - library routines that handle binary bits
- File
And Device I/O - library routines that let programs use the
hard and floppy drives, screen, keyboard, and other computer
hardware.
- Mouse Support - library routines that let your programs use the
features of the mouse, like clicking buttons and pointer movement.
- Operating
System - library routines that handle your program's relationship
with the operating system on your computer.
- Special
Machine Dependent Routines - library routines that allow direct access
to computer resources normally accessed by Euphoria.
- Debugging - library routines that lets you do diagnostics on the program while it runs.
- Graphics And Sound - library routines that lets your programs perform multimedia features like graphics and sound.
-
Machine Level Interface - library routines that allow access to low level (machine language) features of the computer
We
will cover most of the library routines in this tutorial, except
those that involve machine language work. These are for the
advanced programmer
who has machine language experience. The library routines will
be introduced based on the subject in each of the next chapters
ahead. Where are the library routines stored? Well, they are
defined in one of two places. Some are written in machine language
and are defined in the interpreter EX.EXE. The rest are written in
the Euphoria programming language. Library routines written in
Euphoria require a special Euphoria programming statement called
an "include" statement. The syntax is listed below:
include (file name).e
To use library routines written in Euphoria, an include statement MUST
be at the top of your program or they will not work. When you use
EX.EXE to interpret your program, or BIND.BAT to create a machine
language replica, (file name) is referenced to get the programming statements of (file name).e,
the library routine(s) stored in there. Include files always end in
an .e extension. The name of the include file will depend on the
Euphoria-coded library routine you are using. Euphoria (Version
1.5) has 8 include files, listed below with a brief description of
each:
GRAPHICS.E - Graphics And Sound
SORT.E - Sorting Routine
GET.E - Input And Conversion Routines
MOUSE.E - Mouse Routines
FILE.E - Random Access File Operations And Directory Functions
MACHINE.E - Machine Level Programming For 386's And Higher
WILDCARD.E - Wildcard String Matching And Conversion
IMAGE.E - Graphical Image Routines
When a new Euphoria-coded routine is introduced in the tutorial,
the proper include file will be shown, so don't worry about
remembering all
these include file names. There are two kinds of library routines:
procedures and functions. The only difference between a procedure
and a function is that a function will return a value after it
finishes running, while a procedure does not. To call a library
routine that is a procedure, here is the following syntax below:
procedure name (parameters)
the procedure name is followed by a pair of brackets that contain a
list of values to be sent to the procedure for processing called parameters. Parameters
can be data object values, variable names, or expressions that work out
to a value. Procedures can have no parameters (if they are not
required), or an endless list of parameters. If more than
one parameter is passed to a procedure, each parameter is
separated by commas. If no parameters are passed, then the
brackets are placed back-to-back with no spaces in-between, like
"()". The following are actual Euphoria procedures that you will be
learning ,about later. They are being presented here for you to
see how each accepts parameters. Don't worry about what they do
just yet.
print(1,"Hey, now!")
clear_screen()
pixel({BRIGHT_BLUE,BRIGHT_RED,BRIGHT_GREEN},{100,150})
Functions are identical in appearance to procedures, so the
syntax of a function isn't hard to learn if you already understand
the syntax of procedures. However, a function requires a slight
addition to its syntax in order to return a value, as previously
mentioned. The syntax of a function is on the next page.
receiving variable = function name (parameters)
Because a function returns a value after processing is complete, it
must be used in an assignment statement with a variable on the
left side of the equal sign to receive the returned value. You
could say a function is very similiar to an expression because,
like an expression, it returns a single value. Functions can
return a value of any data object type (an atom or sequence). Some
Euphoria functions can return both data object types. For this
reason, it is advisable to have a receiving variable of type
object to handle both types of return values. You may remember, in
our discussion on declaring variables, we mentioned object type
variables are used when the type of data being returned from a
program process is unknown. Now you know why object type
variables are important when dealing with functions! Because a
function's syntax requires the use of an assignment statement, and
because it returns a single value, it can be part of an
expression that is itself evaluated to a single value. Here are
some examples of actual Euphoria functions, the last of which is
being used as part of an expression. Again, don't try to
understand the meaning of each function.
index_bitmap = read_bitmap("index.bmp")
pressed_key = get_key()
computed_result = sqrt(25) * (30 + units)
While a function can modify the original value of a receiving
variable, both procedures and functions do not modify the
parameter values that are passed to them. Also, procedure and
function names follow the same rules that variable names must
adhere to. The following abbreviations will be used every time a new
library routine is introduced to you. Take a moment to look them
over:
a - either an atom data object or a variable of type atom
i - either an integer data object or a variable of type integer
o - either an object data object or a variable of type object
s - either a sequence data object or a variable of type sequence
ra,ri,ro,rs - receiving variable, the second letter means variable type.
If any of these abbreviations are used more than once, a number
will follow the letter (i.e. s1, s2, s3 or o1, o2) to separate the
parameters apart.
The next chapter will begin your learning of Euphoria library routines!
ToC
Displaying Data On The Screen
Now that you have reached this current level
of understanding about the Euphoria programming language, the
tutorial's style of teaching is going to change. Instead of
reading program examples on the screen, you will now be able
to execute actual Euphoria programs and view their source
code. This approach is necessary, as these advanced features of
Euphoria need more than reading text in the tutorial in order to
be clearly understood. The program examples you have studied
through the past chapter have one thing in common. All the data
they process is stored in the body of the program, in the form of
assignment statements. In real life, this is not the case. Today's
programs do not keep data as part of the source code, but instead
obtain it from elsewhere. The idea of retrieving and storing data
outside the program makes good sense, for three reasons. First of
all, the data can be edited without the program itself being
changed. Second, if more than one program uses the same kind of
data (like an employee list), there is no data duplication. The
programs share the same data instead of each program having its
own copy of the data. Finally, having the ability to send data
outside the program means the data can be made presentable
to human eyes.
Up to now, you have taken the word of this tutorial
and its author that what is stored in the variables of example
programs was the case. In real life, people need more than that in
the form of printed reports and spreadsheet figures on the
screen. Data that is accepted by a program is called "input". Data
that is produced by a program is called "output". Programs
send data to and receive data from components of your
computer called "devices". These device can either be "input
devices" that send data to a program, or "output devices" that
receive data from a program. There is a third type of device
called an "I-O device", which is both an input and an output
device, but this type will be covered in a later chapter. The
keyboard and mouse are examples of input devices. The computer
screen and printer are examples of output devices. When a Euphoria
program starts running, some devices are automatically allocated
for use. These three devices are listed below:
0 or standard input, the keyboard by default
1 or standard output, the screen by default
2 or standard error, the screen by default
Devices 1 and 2 are actually the same, but are defined separate in
case there is a reason one type of screen output should be made
distinct from the rest. Notice the phrase "by default". This
implies we can make the numbers mean something else other than
keyboard and screen. In a later chapter, you will be shown exactly
how to do this. To keep it simple for now, understand that 0 means
"keyboard", and 1 means "screen". This chapter will introduce
screen output, while a future chapter will focus on keyboard
input. Now that you are familiar with Euphoria's device numbers,
let's introduce you to your very first Euphoria library routine,
called print():
print(1,o)
print()
displays a Euphoria data object on the screen. The object
is displayed at the current cursor position. print() displays
data on the screen "as is", meaning what is displayed on the
screen is the actual value of the data object. With sequences,
you will be able to see the braces and commas, even if you used a
character string to represent the value. If you click the demo button
on the remote, you will be able to run and view the source of demo
programs that use print().
Demo program 1
atom some_atom_value
some_atom_value = 134.45
print(1,some_atom_value)
Demo program 2
sequence some_sequence_value
some_sequence_value = {1,2,3,4,3,2,1}
print(1,some_sequence_value)
Demo program 3
print(1,36/2)
Demo program 4
sequence my_name
my_name = "David Gay"
print(1,my_name)
One minor drawback with print() is that it displays the actual
values of atom and sequence data objects. As a result, any data
object values meant to be shown on the screen as ASCII characters
cannot be displayed using print(). However, Euphoria has another
library routine that can display screen output in human-readable
form:
puts(1,o)
Because puts() displays data as text characters instead of actual
values, cursor control codes such as line feed (10), carriage
return (13), and tab control (9) can be utilized. You can have
text strings separated by lines or formatted in different tab
columns. This makes puts() useful in print control as a
result. Two notes about puts(). First of all, this library routine
can only print one-dimensional sequences (those composed of atoms
as elements). This makes sense, as text character strings are
really a representation of one dimensional sequences. Also, any
attempt to print an atom value larger than 255 will result in an
incorrect character value being displayed. Again, this makes sense
because the ASCII code set goes from 0 to 255. Some demo
programs are available from this screen to show how puts() works.
Demo program 5
atom a_character
a_character = 'A'
puts(1,a_character)
Demo program 6
sequence a_string
a_string = "Utter Nonsense"
puts(1,a_string)
Demo program 7
puts(1,"Column 1\tColumn 2\tColumn 3\n\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
It is sometimes necessary to display additional screen output to
help clarify information. This process is called "print
formatting". Examples of print formatting are adding commas and
dollar signs to financial figures or to limit the number of
decimal places shown in scientific data. We can use the library
routine printf() to display formatted screen output:
printf(1,s,o)
printf() prints data (shown as o)
as a character string on the screen just like puts(). Before it
does this, however, it formats the data using a format string
(shown as s).
The format string must contain special format codes that will edit
the way the data will appear on the screen, and optionally, some
extra text. The data to be displayed can either be a single atom
value, or a sequence that contains a list of values to be edited
using the format string. The number of format codes used will
depend on the number of values that are to be edited and then
printed. Let's look more closely at the format codes available for
the programmer to use:
%d - this will edit any atom values to appear as a decimal integer.
Decimal integers are the numbers we are all familiar with, using a numbering system of 10 digits from 0 to 9.
%x - this will edit any atom values to appear as a hexadecimal integer.
Hexadecimal integers are numbers that are made up of digits from 0 to
9, A, B, C, D, E, and F. It is also known as a "base-16" numbering
system (our decimal system is a "base-10" numbering system).
%o - this will edit any atom values to appear as an octal integer.
Octal integers are numbers that are made up of digits from 0 to 7. It is also known as a "base-8" numbering system.
%s - this will edit any sequence values to appear as character strings.
This works just like puts() but with extra formatting options.
%e - this will edit any atom values to
appear as a floating point number using exponential (or standard)
notation.
%f - this will edit any atom values to appear as a floating point number,but not using exponential notation.
%g - this will edit any atom values to
appear as a floating point number, using either the format offered
by %e or %f, whichever works best for the
display of the value.
%% - this will display the percentage character.
Format codes can have a field width number added to control the
number of displayed characters. For example, %6d means a minimum
display size of 6 digits. If the number ends up smaller than 6
digits in this example, it will be right-justified with spaces.
Adding a - in front of the number, like %-6d, will make
it left-justified. If a zero is placed in front of the
number, like %06d, zeroes instead of blanks will be used to fill
up any leftmost field positions not used by the value. Placing
a plus sign (+) in front of the number, like %+6d, will make
positive values appear with a leading plus sign. Normally,
positive values are displayed without a sign.The addition of a
field size is not limited to decimal integers alone. You can even
add them to other format codes like %s or %f (though using
to fill any unused leftmost positions of a string with zeroes
is unusual, even if it is considered valid). With floating point
format codes (%f), the field size can be a
decimal number. The number to the LEFT of the decimal point is the
field size, while the number to the RIGHT of the decimal point is
the precision. Precision means how many digits of the decimal
fraction you want to have shown. For example, %7.3f will show the
floating point value 13.1705 as 13.170 with a leading blank (
13.170). The decimal fractions are not cut off, however: they are
rounded up. This means %6.2f will display the value 899.987 as
899.99 with no leading blanks. Remember when we stated the data to
be displayed in printf() is either a single atom value or a
sequence representing a list of values to be displayed? For this
reason, if you are attempting to display a SINGLE sequence value
as a string, you must make it the only element of
a sequence. This step is necessary, otherwise the sequence
itself will be treated as a list of values and only the first
character will be printed:
printf(1,"%s\n","Euphoria") - Only "E" is printed
printf(1,"%s\n",{"Euphoria"}) - "Euphoria" is printed
A series of demo programs have been assigned to this page to help clarify printf().
Demo program 8
atom atom_value
integer integer_value
atom_value = 1233.14
integer_value = 255
print(1,atom_value)
printf(1,"\n is shown as %d when using %%d", atom_value)
printf(1,"\n is shown as %e when using %%e", atom_value)
printf(1,"\n is shown as %f when using %%f\n\n", atom_value)
print(1,integer_value)
printf(1,"\n is shown as %x when using %%x", integer_value)
printf(1,"\n is shown as %o when using %%o\n\n", integer_value)
atom_value = 'A'
print(1,atom_value)
printf(1,"\n is shown as %s when using %%s\n", atom_value)
Demo program 9
puts(1,"Field Width Using %9s\n")
puts(1,".........\n")
printf(1,"%9s", {"CAT"})
puts(1,"\n\n")
puts(1,"Field Width Using %-9s\n")
puts(1,".........\n")
printf(1,"%-9s", {"CAT"})
puts(1,"\n\n")
puts(1,"Field Width Using %4d\n")
puts(1,"....\n")
printf(1,"%4d", 123)
puts(1,"\n\n")
puts(1,"Field Width Using %04d\n")
puts(1,"....\n")
printf(1,"%04d", 123)
puts(1,"\n\n")
puts(1,"Field Width Using %+4d\n")
puts(1,"....\n")
printf(1,"%+4d", 123)
puts(1,"\n\n")
Demo program 10
puts(1, "How Decimal Precision Affects Floating Point Values\n\n")
printf(1,"%f <- Using Straight %%f\n", 123.7651)
puts(1,".......\n")
printf(1,"%7.3f <- %%7.3f\n", 123.7651)
printf(1,"%7.2f <- %%7.2f\n", 123.7651)
printf(1,"%7.1f <- %%7.1f\n", 123.7651)
printf(1,"%7.0f <- %%7.0f\n", 123.7651)
We would normally explore the other side of screen output by
introducing library routines that accept data from the keyboard.
However, because some keyboard library routines require a program
to execute a block of statements based on a condition or for a set
number of times in order to work properly, a detour is needed. The
next chapter will show you how to change the way your program
executes.
ToC
Program Branching And Looping
The demonstration programs in this tutorial, while helpful in
understanding Euphoria, are not good models of how the majority
of programs work today. Program execution is not a straight
line from program start to program end. A program may execute a group
of statements meant to handle an anticipated condition, or
repeat those statements until a condition is met. In short,
program execution is a series of backtracks and detours. A
group of statements that runs only when a specific condition is met
is called a program branch. A group of statements that is meant to
repeat while a condition is being met is called a program loop.
Let's begin our learning by introducing the "if" statement, which is the workhorse of program branching:
if expression then
one or more Euphoria statements
end if
The "if" statement will execute one or more Euphoria statements only if expression evaluates to be true. If the expression evaluates to to
be false, program control will skip the conditioned statements
and then resume with the first Euphoria statement following the end if line. The
expression of an "if" statement can be any arithmetic, logical,
or relational expression, or even a constant value. Most of the
time, however, an expression is either a simple logical expression
or a larger relational expression linking logical expressions
together. A demo program is available to demonstrate a series of "if"
statement examples. But before you run the demo, view the source
to see if you can guess which of the puts() lines will be printed.
Demo program 11
atom first, second, sum
first = 36
second = 1.5
sum = first * second
if second >= 1.4 and second <= 1.6 then
puts(1,"Condition 1 is true\n")
end if
if sum = 54 then
puts(1,"Condition 2 is true\n")
first = 63
end if
if first = 36 then
puts(1,"Condition 3 is true\n")
end if
The "if" statement can be used to execute a group of statements for
both false and true outcomes of a condition. One way to do this is
to write a second "if" statement that checks for the opposite of
the first "if" statement.
if speed > 65 then
puts(1,"You are driving too fast!\n")
end if
if speed <= 65 then
puts(1,"Thank you for driving within the speed limit!\n")
end if
The use of two "if"
statements works, but it is inefficient because both "if"
statements are checked even though only one of two outcomes
will happen. You can't have a condition that is both true and
false. It would be nice to have the "if" statement work like a
toggle switch, executing the right block of statements with only
one condition check. Fortunately, the "if" statement can be
modified to handle two outcomes without using two "if" statements:
if expression then
one or more statements that will only run if expression is true
else
one or more statements that will only run if expression is false
end if
When the "if" statement has an "else" option added, only one of
two things will happen. If the expression portion of the "if"
statement works out to be true, the statements immediately
following below are executed, and the statements under the "else"
line are ignored. If the expression works out to be false, it's
the reverse that happens. The statements under the "if" line are
ignored, but the statements under the "else" line are executed.
Whatever the outcome of the expression, the first statement
following "end if" is always executed once the "if"
statement completes. A demo is available on this screen to show
the use of "else".
Demo program 12
atom character
character = 'a'
if character = 'a' then
puts(1,"********************************\n")
puts(1,"* This line should be printed! *\n")
puts(1,"********************************\n")
else
puts(1,"This line should not be printed!\n")
end if
character = 'b'
if character = 'a' then
puts(1,"This line should not be printed!\n")
else
puts(1,"********************************\n")
puts(1,"* This line should be printed! *\n")
puts(1,"********************************\n")
end if
Using an "if-else" combination can handle situations that result in
only one of two outcomes. For situations that can result in more
than two outcomes, we replace "else" with "elsif".
if expression 1 then
one or more statements that execute if expression 1 is true
elsif expression 2 then
one or more statements that execute if expression 2 is true
elsif expression 3 then
one or more statements that execute if expression 3 is true
elsif expression 4 then
one or more statements that execute if expression 4 is true
end if
Using "elsif" with "if" creates a decision chain, where each of
the expressions are checked one after another, starting with the
first one, expression 1. Once an expression is found to be true,
the statements conditioned to that expression are run. After this,
all other expressions that follow in the "if-elsif" chain are
skipped, and the first statement following "end if" is
executed. Using "if-elsif" allows you to check for a specific set
of outcomes to a situation, rather then using "either this or the
other" kind of logic. It also lets you analyze a problem from a
step-by-step approach, eliminating the need for designing complex
expressions and typing out many "if" statements. A demo program is
available that shows one use of "if-elsif" chains.
Demo program 13
atom character
character = 'M'
if character >= 'A' and character <= 'J' then
puts(1, "This line should not be printed\n")
elsif character >= 'K' and character <= 'T' then
puts(1, "********************************\n")
puts(1, "* This line should be printed! *\n")
puts(1, "********************************\n")
elsif character >= 'U' and character <= 'Z' then
puts(1, "This line should not be printed\n")
end if
You can have what is called nested "if" statements, where one
"if" statement leads to a second "if" statement when its
expression is true:
if expression 1 then
if expression 2 then
one or more statements that run only if expression 2 is true
end if
end if
In the case of the example on the previous page, it is the same as:
if expression 1 and expression 2 then
one or more expressions to be run only if both expressions are true
end if
However, using nested "if" statements allow you more
flexibility, particularly when you want to mix "if" statements,
library routine calls, and assignment statements in a block
of conditioned statements to be run. It also gives you the option of
avoiding the use of "if" statements that have complex relational
expressions made up of smaller expressions. When using nested "if"
statements, make sure each "if" has a matching "end if" for each
"if" level, just like the example you saw earlier. Make sure that
any optional "else" and "elsif" used are on the correct level as
the "if" statement they are associated with.
You've probably noticed, in all of the "if" statement examples
we have shown, we used expressions that involved atom variables
and values. The reason for this is because "if" statements cannot
handle direct comparisons using sequence data objects. However,
there is a library routine that can help you get around this
problem:
ri = compare(o1,o2)
compare() takes two data objects, o1 and o2, and compares them, returning an integer value that represents the result of the comparison. If o1 is equal to o2, 0 is returned. If o2 is smaller than o1 , -1 is returned. If o2 is larger than o1, 1 is returned. compare()
can compare two atoms, two sequences, or a sequence and an atom.
Atoms are always assumed to be smaller than sequences in value.
Where sequence comparison is concerned, it's an element by element
comparison until a difference is found, either in the length of
the sequences or a different element value. So, to compare one sequence
with another, you simply follow the format shown in this example
below:
sequence my_name
my_name = "David Gay"
if compare(my_name, "David Gay") = 0 then
puts(1,"Hi, David!\n")
else
puts(1,"Who the heck are you?\n")
end if
A program demo is available to help clarify compare() if needed.
Demo program 14
sequence animal1, animal2
animal1 = "cat"
animal2 = animal1
if compare(animal1,animal2) = 0 then
puts(1, "These strings are the same\n")
else
puts(1, "These strings are unequal\n")
end if
animal2 = "CAT"
if compare(animal1,animal2) = 0 then
puts(1, "These strings are the same\n")
else
puts(1, "These strings are unequal\n")
end if
Program branching is only one part of changing the way your program
runs. You can also make a group of statements repeat for as long
as an expression remains true. This is done by using the "while"
statement:
while expression do
one or more statements that are executed while condition is true
end while
When the "while" statement first starts, it checks expression to see if
it is true. If it is not, the next programming statement following end while is
executed. If the expression is true, the "while" statement will
continue to repeat the statements between "while" and "end
while" until the expression portion of
the "while" statement becomes false. This tells you that
the repeating statements must be able to make the expression
change to false, or the program will be stuck in an
endless "while" loop. A demo program using a "while" statement is
available.
Demo program 15
atom count
puts(1,"Hello, I'm Sparky I, The Counting Euphoria Program\n")
puts(1,"Watch me count to 10!\n\n")
count = 1
while count < 11 do
printf(1,"%d\n",count)
count = count + 1
end while
If you want to repeat a group of statements for a specific number of times, Euphoria offers the "for" statement:
for ra = start value to end value by increment do
one or more statements to repeat until ra > end value
end for
When the "for" statement starts, it declares a temporary index variable, ra, assigning it a start value. It then processes the statements down to the end for line. Once it reaches that point, it changes ra's value by adding the increment. The program then loops back up to check if ra is larger than the end value. If not, the statements are processed again, and ra is changed by adding the value increment. This loop will keep repeating until ra is larger than end value. When this occurs, ra
is "undeclared", and the program continues with the next statement
after the "end for" line. The increment portion of the "for" statement
is an optional feature.
If left out, the default increment is 1. You can have a negative
increment value, so the "for" statement counts down instead of up,
but set the end value of the "for" statement to be smaller than
the start value. Unlike the "while" statement, the "for" statement
cannot be locked into an endless loop, because the expression
change is handled automatically. Also, the index variable cannot
be changed by any statements within the loop, but it can be
referenced for use (such as an element index in a sequence). If
the value in the index variable is required for later use,
you should save it in a variable before the loop ends, because both
the index variable and its value will vanish when the "for"
statement ends. This temporary index variable has an attribute
called "scope", meaning it is accessible only for a specific
duration in the program . This topic will be revisited later in
this tutorial. In the meantime, run a program demo now to
demonstrate how the "for" statement can be used.
Demo program 16
puts(1,"Hello, I'm Sparky II, The Counting Euphoria Program\n")
puts(1,"Watch me count to 5, then back again to 1!\n\n")
for count = 1 to 5 do
printf(1,"%d\n",count)
end for
puts(1,"\n")
for count = 5 to 1 by -1 do
printf(1,"%d\n",count)
end for
Both the "for" and "while"
statements can be nested, meaning that you can have smaller loops
within larger ones. However, the nested "while" and "for"
statement levels should have correctly matching "end while" and
"end for" statements. You can force both a "while" and a "for"
statement to end early using the "exit" statement. The loop ends
at the moment the "exit" statement is executed. The program will
then resume at the next statement following "end for" or "end
while". The "exit" statement is best used as either an "emergancy
brake" when something unexpected comes up, or for creating a loop
that needs to end for a set of reasons uniquely separate from each
other. A program demo shows the "exit" statement in action. You
have now learned everything you need to know to control the course
of program execution. These tools will be valuable in the next
chapter, when you learn how to accept data from the keyboard.
Demo program 17
atom pet_index
sequence pets
puts(1,"Examples Of Animals You Can Keep As Pets\n")
puts(1,"========================================\n\n")
pets = {"Cat",
"Dog",
"Hamster",
"Rat",
"Snake",
"Parrot",
"Budgie",
"Unelected Politician",
"*END*"}
pet_index = 1
while 1 do
if compare(pets[pet_index],"*END*") = 0 then
exit
else
puts(1,pets[pet_index])
puts(1,"\n")
end if
pet_index = pet_index + 1
end while
puts(1,"\nList Completed!\n")
Displaying data on the screen is only one side
of program-human interaction. While it is great that we can
now write programs that present text on the
screen, our programs can be even more useful if they can accept
data for processing later. A program could be written to accept
keyed-in monthly expenses and compute either a surplus or
a deficit in your household budget. Euphoria has a set of
library routines to handle keyboard input. Here's the first one
listed below:
include get.e
ri = wait_key()
wait_key() causes the program
to pause until a key is pressed, then stores that value in an
integer variable to the left of the equal sign. You will notice that wait_key()
is an example of a library routine that is externally defined in
an include file, called get.e. get.e must be present at the top of
your program in order to use this library routine. If a key
generating a displayable character (alphabet, numbers,
symbols, and punctuation) or cursor control (tab, linefeed,
backspace, etc) is pressed, the value stored in the receiving
integer value is the ASCII code for that value. This means you can
use puts() or print()
to display the value on the screen as a text character, or move
the cursor. Keys like the function keys from F1 through F12, arrow
keys, insert and delete keys, and any key pressed while the ALT
key is pressed down generate a value higher than 255. These keys
are meant to be defined for the use of the programmer and do not
generate a displayable screen character. A demo program
showing how wait_key() is used is available on this page.
Demo program 18
include get.e
integer keystroke
puts(1,"Please press a key on the keyboard\n")
keystroke = wait_key()
puts(1,"\nThank you!\n")
printf(1,"You pressed the %s key!\n",keystroke)
It is sometimes necessary to
read in an entire character string instead of a single character.
Euphoria has a library routine that performs this:
ro = gets(0)
Like wait_key(), gets() pauses the program run for keyboard data. Unlike wait_key(),
it is not a single value but a string of data that is stored in a
variable at the left of the equal sign. However, notice that
the receiving variable is an object type variable. This is
necessary because a value of -1 (meaning that no keyboard data
string was retrieved) may also be returned. It's also important to
note that the Enter key that you pressed to end the string to send
to gets() is a part of the
string, at the very end as value 10. The receiving object variable
will contain a sequence composed of atoms that represent ASCII codes.
This means puts() and printf() can be used to display the data received by gets(). A demo program is available to show one use of gets() from this screen.
Demo program 19
object name, city
puts(1, "Hello, what is your name?\n