A Beginners Guide To Euphoria

From the Euphoria web site:

Euphoria is a simple, flexible, and easy-to-learn programming language. It lets you quickly and easily develop programs for Windows, DOS, Linux and FreeBSD. Euphoria was first released in 1993. Since then Rapid Deployment Software has been steadily improving it with the help of a growing number of enthusiastic users. Although Euphoria provides subscript checking, uninitialized variable checking and numerous other run-time checks, it is extremely fast. People use it to develop Windows GUI programs, high-speed DOS games, and Linux/FreeBSD X Windows programs. It is also very useful for CGI (Web-based) programming.

Euphoria is free and open source software. You can download it here.

This is a conversion of David Gay's interactive DOS tutorial, which can still be downloaded from the archive. It overcomes some limitations of the original format, such as not being able to print out any text or demo programs (which are here listed in the body of the text), and of course, the tutorial can now easily be read by Linux/FreeBSD users. The tutorial assumes no prior knowledge of programming in any language, and is a fine complement to the official Euphoria documentation. I suggest you copy and paste each demo program to your favourite editor as you come to it, then add these lines at the top:
   with trace
   trace(1)
This will enable you to step through each line of code and see the variables change as the program runs. Note that the content of the tutorial has been faithfully preserved from David's executable, so occasional references to "the console", etc (which may give rise to some puzzlement) are a reflection of the original format. There have been many additions and enhancements to the language during the years since 1997 when this tutorial was originally written. Chapters 21 and 22 should really be re-named 'Euphoria And OS', to reflect the fact that Euphoria is now multi-platform. The download now includes a database system (EDS), and more recently, multi-tasking was built-in to the interpreter. In spite of this, the tutorial still stands as the most comprehensive introduction to the basics. The later chapters were copied directly from a screen, so a few errors may have crept in. Also, although the demo programs should work ok, I haven't tested them all. If you find any errors/bugs, drop me a line at suninyoureye@linuxmail.org, and I'll fix them.

Table of Contents:



Euphoria Programs And The Programming Language

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:
  1. 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).
  2. 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.
  3. 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:
 
  1. Predefined Types - library routines that test the type of a data
  2. Sequence Manipulation - library routines that offer advanced sequence handling features
  3. Searching and Sorting - library routines that compare, look for, and sort data objects
  4. Pattern Matching - library routines that can convert alphabet case and allow pattern matching in a string of characters
  5. Math - library routines that handle advanced mathematic formulas far beyond the power of the math operators +,-,/, and *
  6. Bitwise Logical Operators - library routines that handle binary bits
  7. File And Device I/O - library routines that let programs use the hard and floppy drives, screen, keyboard, and other computer hardware.
  8. Mouse Support - library routines that let your programs use the  features of the mouse, like clicking buttons and pointer movement.
  9. Operating System - library routines that handle your program's relationship with the operating system on your computer. 
  10.  Special Machine Dependent Routines - library routines that allow direct access to computer resources normally accessed by Euphoria.
  11.  Debugging - library routines that lets you do diagnostics on the program while it runs.
  12. Graphics And Sound - library routines that lets your programs perform multimedia features like graphics and sound.
  13. 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