Ordinary division in Euphoria looks like this:
atom result = 7 / 3 ? result -- 2.333
Euphoria always produces a proper decimal result for divisions--just as you would expect.
Using traditional long division it would look like:
The floor() function is used to truncate a decimal value to an integer:
atom quotient = floor( 7 / 3 ) ? quotient -- 2
The remainder() function is used to find whats left over, before you try and calculate a fractional value for the division. (This is also known as the modulus operation .)
atom remain = remainder( 7 , 3 ) ? remain -- 1
So 7 divided by 3 is 2 with 1 left over.
The remainder() function turns out to be surprisingly useful. For example, you can check whether one number is divisible by another--if remainder(x, y) is zero, then x is divisible by y.
Also, you can extract the right-most digit or digits from a number. For example, remainder(x ,10 ) yields the right-most digit of x (in base 10). Similarly remainder( x, 100 ) yields the last two digits.
You may mix any atom and any integer in a division calculation and the result could be either an integer or decimal value depending on the answer. Remember that you can not assign a decimal value to an integer without causing a type-check error. Use floor() to make sure values assigned to integers are integers.
A boolean expression is an expression that is either false or true.
Euphoria has no explicit boolean-type. Euphoria will display 0 as false and 1 and as true. Actually Euphoria is a bit more generous: you can use 0 as false and not 0 as true, (thus any value other than zero will be true.)
The operator ( = ) compares two atom values and produces a boolean result:
? 5 = 5 -- 1 ? 5 = 6 -- 0
In the first command, the two operands are equal, so the expression evaluates to 1 ( true ); in the second command, 5 is not equal to 6, so we get 0 ( false ). The ( = ) operator is one of the relational values:=, !=, >, <, >=, and <=. operators; the complete set is:
x = y -- x is equal to y x != y -- x is not equal to y x > y -- x is greater than y x < y -- x is less than y x >= y -- x is greater than or equal to y x <= y -- x is less than or equal to y
Note that the ( = ) has two distinct uses: assignment and atom comparison. Context is sufficient to distinguish between these two uses.
Although these operations are probably familiar to you, the Euphoria symbols are different from the mathematical symbols.
It is safest if you consider the use of ( = ) only for comparisons between atoms. Relational operators can be used with same length sequences when you are doing atom comparisons for every element.
For two sequences that are the same length, then the relational operators work like this:
sequence x, y x = { 1, 2, 9 } y = { 4, 5, 9 } ? x = y -- { 0, 0, 1 }
The ( = ) test is applied to each element of the sequence, one after the other. In reality you are only doing atom comparisons.
sequence x, y x = { 1, 2, 9 } y = { 4, 5, 9 } ? x < y -- { 1, 1, 0 } sequence w w = { 1, 2, 3 } = { 1, 2, 4 } ? w -- { 1, 1, 0 }
The first ( = ) is an assignment and the second ( = ) is a relational operator. Even though the last example is legal it is best to avoid one line commands like this--it's just too easy to misinterpret.
Since a sequence could be arbitrarily complex you need special functions to compare them. You can determine if two sequences are the same with the equal() function:
sequence x, y x = "apple" y = "orange" ? equal( x, y ) -- 0
A more versatile test is the compare() function:
sequence x, y x = "apple" y = "orange" ? compare( x, y ) -- -1
This tells you that x is "less than" y when you compare these two sequences. compare() first compares the number of elements and then compares the sequences element by element.
There are are four logical operators : and, or, xor and not. The semantics (meaning) of these operators is similar to their meaning in English.
Truth tables are the best way to understand how logical operators work. They show all possible outcomes:
1 and 1 -- 1 (true) 1 and 0 -- 0 (false) 0 and 1 -- 0 (false) 0 and 0 -- 0 (false) 1 or 1 -- 1 (true) 1 or 0 -- 1 (true) 0 or 1 -- 1 (true) 0 or 0 -- 0 (false) 1 xor 1 -- 0 (false) 1 xor 0 -- 1 (true) 0 xor 1 -- 1 (true) 0 xor 0 -- 0 (false) not 1 -- 0 (false) not 0 -- 1 (true)
For example, x > 0 and x < 10 is true only if x is greater than 0 and less than 10.
remainder( n, 2) = 0 or remainder( n, 3) = 0 is true if either of the conditions is true, that is, if the number is divisible by 2 or 3.
Finally, the not operator negates a boolean expression, so not(x > y) is true if (x > y) is false, that is, if x is less than or equal to y.
Strictly speaking, the operands of the logical operators should be boolean expressions, but Euphoria is not very strict. Any nonzero number is interpreted as "true."
atom x = 5 ? x and 1 -- { 1 } atom y = 0 ? y and 1 -- 0
In general, this sort of thing is not considered good style. If you want to compare a value to zero, you should do it explicitly.
In order to write useful programs, we almost always need the ability to check conditions and change the behavior of the program accordingly. Conditional commands give us this ability. The simplest form is the if command:
if x > 0 then puts(1, "x is positive" ) end if
The boolean expression after the if command is called the condition . It is evaluated and tested as a boolean value. If it is true, then the code-block following then gets executed. If not, nothing happens.
if CONDITION then CODE-BLOCK end if
The if command may contain many commands within it's body. Commands like this are also known as compound commands . There is no limit on the number of commands that can appear in the code-block code-block of an if command. You can even write an if command with no commands in the code-block. In this case nothing happens.
Occasionaly, it is useful to write compound commands with nothing in the body to represent code that you intend to write, but haven't finished yet
if x < 0 then -- need to handle negative values end if
An if command can be used to solve the problem of comparing two atomic values that are very close in magnitude to each other. Be careful when comparing decimal values:
atom x = 1.000000000001 atom y = 1.000000000000 ? x = y -- 0
The two values are not identical. However, for a particular problem it may be important to consider them to be identical; the values are "close enough." If this is important, then test for "closeness" and not equality:
include std/math.e if abs(x - y) < 0.000000001 then puts(1, "values are close enough" ) end if
The abs() routine finds the absolute value of the difference between x and y. Forgetting to test for closeness can result in programs that fail or go into infinite loops.
A second form of the if command is alternative execution , in which there are two possibilities and the condition determines which one gets executed. The syntax looks like this:
if remainder( x, 2 ) = 0 then printf(1, "%d is even", x ) else printf(1, "%d is odd", x ) end if
If the remainder when x is divided by 2 is 0, then we know that x is even, and the program displays a message to that effect. If the condition is false, the second set of commands is executed. Since the condition must be true or false, exactly one of the alternatives will be executed. The alternatives are called branches , because they are branches in the flow of execution.
As an aside, if you need to check the parity (evenness or oddness) of numbers often, you might wrap this code in a function:
function printParity( atom x ) if remainder( x, 2 ) = 0 then printf(1, "%d is even", x ) else printf(1, "%d is odd", x ) end function
For any value of x, printParity() displays an appropriate message. When you call it, you provide any expression evaluating to an atom as an argument.
printParity( 17 ) printParity( y+1 )
Sometimes there are more than two possibilities and we need more than two branches. One way to express a computation like that is a chained conditional
if x < y then printf(1, "g is less than %g", { x, y } ) elsif x > y then printf(1, "%g is greater than %g", { x,y } ) else printf(1, "%g and %g are equal", { x,y } ) end if
elsif is an abbreviation of "else if." Again, exactly one branch will be executed. There is no limit of the number of elsif commands, but the very last branch has to be an else command:
if choice = 'A' then functionA() elsif choice = 'B' then functionB() elsif choice = 'C' then functionC() else puts(1, "Invalid choice." ) end if
Each condition is checked in order. If the first is false, the next is checked, and so on. If one of them is true, the corresponding branch executes, and the command ends. Even if more than one condition is true, only the first true branch executes.
One conditional can also be nested within another. We could have written the trichotomy example as follows:
atom x = 5 atom y = 6 if x = y then puts(1, "x and y are equal" ) else if x < y then puts(1, "x is less than y" ) else puts(1, "x is greater than y" ) end if end if
The outer conditional contains two branches. The first branch contains a simple output command. The second branch contains another if command, which has two branches of its own. Those two branches are both output commands, although they could have been conditional commands as well.
Although the indentation of the commands makes the structure apparent, nested conditionals become diffcult to read very quickly. In general, it is a good idea to avoid them when you can.
Logical operators often provide a way to simplify nested conditional commands. For example, we can rewrite the following code using a single conditional:
if 0 < x then if x < 10 then puts(1, "x is a positive single digit." ) end if end if
The output command is executed only if we make it past both the conditionals, so we can use the and operator:
if 0 < x and x < 10 then puts(1, "x is a positive single digit." ) end if
The return command allows you to terminate the execution of a routine before you reach the end. For procedures, return leaves the routine. For functions, return must also return a value. One reason to use it is if you detect an error condition:
procedure printLogarithm( atom x ) if x <= 0 then puts(1, "Positive numbers only, please.") return else atom result = log(x) printf(1, "The natural logarithm of x is %g", result ) end if end procedure printLogarithm( 5 ) --The natural logarithm of x is 1.60944
The procedure printLogarithm() takes the argument x. The first thing it does is check whether x is less than or equal to 0, in which case it displays an error message and then uses return to exit the procedure. The flow of execution immediately returns to the caller, and the remaining lines of the procedure are not executed.
It is legal for one routine to call another; it is even legal for a routine to call itself. It may not be obvious why that is a good thing, but it turns out to be one of the most magical things a program can do. For example, look at the following function:
procedure countdown( atom n ) if n <= 0 then puts(1, "Blastoff!" ) else print(1, n ) puts(1, "\n" ) countdown(n-1) end if end procedure countdown( 4 )
countdown() expects the parameter, n, to be a positive integer. If n is 0, it outputs the word, "Blastoff!" Otherwise, it outputs n and then calls a function named countdown()--itself--passing n-1 as an argument.
If n is 0, it outputs the word, "Blastoff!" Otherwise it outputs n and then calls a function named countdown()--itself--passing n-1 as an argument.
What happens if we call this procedure like this:
countdown( 3 )
And then you're back in where you started. So, the total output looks like this:
3 2 1 Blastoff!
A function that calls itself is recursive, recursion .
As another example, we can write a procedure that outputs a string n times.
procedure printN( sequence s, atom n ) if n < 0 then return end if puts(1, s ) printN( s, n-1 ) end procedure
If n < 0 then the return command exits the procedure. The flow of execution immediately returns the caller, and the remaining lines of the procedure are not executed.
The rest of the procedure is similar to countdown(): if n is greater than 0, it outputs s and then calls itself to output s n-1 additional times. So the number of lines of output is 1 + (n - 1), which adds up to n.
For simple examples like this, it is probably easier to use a for loop. But we will see examples later that are hard to write with a for loop and easy to write with recursion, so it is good to start early.
Previously we used a stack diagram to represent the state of a program during a routine call. The same kind of diagram can help interpret a recursive routine.
Every time a routine gets called, Euphoria creates a new routine frame, which contains the routine's local variables and parameters. For a recursive routine, there might be more than one frame on the stack at the same time.
This figure shows a stack diagram for countdown() called with n = 3:
As usual, the top of the stack is the frame for main . It is empty because we did not create any variables in main or pass any arguments to it.
The four countdown frames have different values for the parameter n. The bottom of the stack, where n=0, is called the base case ; the condition that allows a recursive routine to stop. It does not make a recursive call, so there are no more frames.
If a recursion never reaches a base case, it goes on making recursive calls forever, and the program never terminates. This is known as infinite recursion, and it is generally not considered a good idea. Here is a minimal program with an infinite recursion:
procedure recurse() recurse() end procedure
In most programming environments, a program with infinite recursion does not really run forever. After a while Euphoria will stop the recursion and issue the message Killed.
recurse() --Killed
The programs we have written so far are a bit rude in the sense that they accept no input from the user. They just do the same thing every time.
Euphoria provides built-in functions that get input from the keyboard. The simplest is called gets(0). When this function is called, the program stops and waits for the user to type something--so type something. When the user presses Return or the Enter key, the program resumes and gets(0) returns what the user typed as a string:
object input input = gets(0) puts(1, input )
What are you waiting for?
-- What are you waiting for?
The gets() function is versatile, by changing the argument you can input data from a file. gets(0) is used for input from the keyboard. gets(fn) is used for input from a file, where fn is a filenumber obtained from the open() function.
Before calling gets(0), it is a good idea to print a message telling the user what to input. This message is called a prompt .
object name puts(1, "What... is your name?" ) name = gets(0) puts(1, '\n') puts(1, name )
What... is your name? Arthur, King of Britons! Arthur, King of Britons!
A more convenient way to input a string with prompt_string():
object name name = prompt_string( "What ... is your name?" ) --What ... is your name?
If we expect the response to be an number, we can use the prompt_number() function:
include get.e atom speed speed = prompt_number("What...is the airspeed velocity of an unladen swallow?",{}) ? speed
If the user does not enter a number, then the message is re-displayed.