Keywords: INTEGER, REAL, DOUBLE, CHARACTER, declaration, input, output, assignment
This chapter presents the basic data types in Eiffel:-
INTEGER REAL DOUBLE, CHARACTER.
The syntax and mechanism for declaration, input, assignment, calculation, and output are given for each type. A data declaration reserves storage for a variable, and gives that variable an initial, default value. An input commands get a value from the keyboard, an assignment statement stores a value in a variable, and an output command displays a value on the terminal.
Integers in Eiffel are instances or objects of type INTEGER. In Eiffel, an INTEGER is stored in 32 bits, so an integer can take any value between 2^31 - 1 and - (2^31 - 1); that is, between + 2, 147, 483, 647 and - 2, 147, 483, 647.
The behaviour of an integer is defined by what you can do with it; formally, the behaviour of a class is defined by the operations in that class. The table below shows the symbol, name, and an example expression for each numeric operator defined in class INTEGER:
+ unary plus +6The unary operators + and - take a single value or argument, and return a single value. The unary minus operator returns the negative of its argument, so the value of --3 is 3. All the other INTEGER numeric operators take two values, and return a single value.- unary minus -42
^ exponent 3 ^ 2
* times hours * rate
/ divide total / people
// divisor 365 // 30
\\ modulus hours \\ 12
+ binary plus 3 + total_cost
- binary minus wins - losses
The exponent or power operator takes two numbers, and returns
a new number. Let us call the two integer arguments the number and
the power, and the returned value the result. The value of
the result is found by raising the number to the given power;
several examples are given below. In Eiffel, a number raised to a power
results in a real number, even if the two arguments are both integers.
2 ^ 0 = 1.0 1 ^ 43 = 1.0 0 ^ 28 = 0.0The times operator takes two integers and returns an integer that is the product of its two arguments. The divide operator takes two integers, and returns a single real number that is the value of the first argument divided by the second argument.2 ^ 1 = 2.0 3 ^ 3 = 27.0 -4 ^ 2 = 16.0
2 ^ 2 = 4.0 5 ^ 2 = 25.0 -4 ^ 3 = -64.0
The divisor (symbol //, often named div) and modulus
(symbol \\, often named mod) operators take two integers as arguments.
The divisor returns the number of times that the first integer divides
into the second. The modulus returns the remainder after an integer division.
Examples of the div and mod operators are
12 // 10 = 1 10 goes into 12 1 time (there is 1 10 in 12)All INTEGER operators take one (unary operators) or two (binary operators) integer values as arguments. All but the divide and exponent operators return an INTEGER value; divide returns a REAL value, and exponent a DOUBLE precision real value. The integer operators, the types they use, and the types they produce, are shown below; a list of the argument and the returned types is called the signature of the routine.47 // 10 = 4 10 goes into 47 4 times (there are 4 10s in 47)
53 // 25 = 2 25 goes into 53 2 times
12 \\ 10 = 2 12 is 10 * 1, with 2 left over
47 \\ 10 = 7 47 is 10 * 4, with 7 left over
53 \\ 25 = 3 53 is 25 * 2, with 3 left over
symbol name use result
+ unary plus INTEGER INTEGER- unary minus INTEGER INTEGER
^ exponent INTEGER, INTEGER DOUBLE
* times INTEGER, INTEGER INTEGER
/ divide INTEGER, INTEGER DOUBLE
// divisor INTEGER, INTEGER INTEGER
\\ modulus INTEGER, INTEGER INTEGER
+ binary plus INTEGER, INTEGER INTEGER
- binary minus INTEGER, INTEGER INTEGER
A declaration reserves storage in the computer's memory to store the
value of a variable. Formally, the declaration gives the variable a name,
a type, and an initial value, to fill the three parts of a variable (shown
below). A variable must be declared before it can be used in code.
A variable declared as a feature of a class is called an attribute. An attribute value is a permanent part of an object, and exists as long as the object exists. If a value is defined in the declaration, the variable is a constant, and the value cannot be later changed; a constant must be declared as an attribute.
Some example attributes and constants are shown below, first their data
structure and then their declarations. The first declaration names a single
variable of type INTEGER, the second names three INTEGER
variables, and the third declaration defines an INTEGER constant.
variable length: REAL
variables length, height, width: REAL
constant length: REAL is 4
The syntax of a declaration is a name, followed by a colon, a space, and the type of the variable. Multiple variables of the same type can be declared in a single declaration by writing several names (separated by commas) before the colon; in this case, a comma then a space is placed between two identifiers. Eiffel follows the English convention for text layout, where a punctuation mark (colon, comma) is placed immediately after the word, then a space is placed between the mark and the next word. This makes Eiffel code easy to read, by adopting the natural language conventions that we all know.
A name (identifier) in Eiffel is a sequence of characters. The first character must be a letter ('a' to 'z', A to Z), but the other characters may be letters, digits ('0' to '9'), or the underscore character ('_'). All identifiers (variable, routine, or class name) obey this rule.
The name of an attribute is a noun that describes that attribute. The name is almost always a single word, but there may be situations where a compound name is needed; the convention is to link simple names with an underscore such as tax_rate, my_name, and top_score.. Two atributes in a class cannot have the same name, because the computer needs a unique name for each variable; this situation is known as a name clash. A variable may have the same name as a class. This is not a name clash, because there are not two variables with the same name: there is a variable and a class with the same name. Eiffel has a set of reserved words, listed in Appendix B, that have a special meaning in the language and cannot be used as names.
The type of a variable is written after the name, separated from the name by a colon. Every type in Eiffel is a class, except for generic classes (see Chapter 11). The simple or basic types of variable in Eiffel are INTEGER, REAL, DOUBLE (double precision real numbers), CHARACTER, and BOOLEAN. Class names are always written in upper case in Eiffel.
Eiffel variables get a default value when they are created. Variables of type INTEGER are given an initial value of 0 (zero). The value is changed by an assignment statement.
A constant is declared by giving the name, type, and value in
the attribute declaration. A constant is considered to be part of the class
definition, so it has to be declared as an attribute. The name of
a constant (such as Void) starts with a capital letter, by convention.
Examples of integer constant declarations are
Days_in_year: INTEGER is 365Unique constants represent a set of unique values, and are used to define enumerated data types. A set of values are defined or enumerated in a declaration, the order of these values is defined from left to right in the declaration, and then we can then test and assign such values. The series of names in the declaration are given a series of INTEGER values that are guaranteed to be unique and increasing, but whose exact value is unknown. They represent enumerated types, when the set of possible values can be enumerated and no further detail is needed. The declaration has the formDays_in_week: INTEGER is 7
Red, Orange, Yellow, Blue, Green, Indigo, Violet: INTEGER is uniqueEach name is a constant, so it can be assigned to an INTEGER variable, and that value can be tested, as in if colour < Yellow then ....
Variable names begin with a lower case letter, constants with an upper case letter, and compound names are connected by an underscore. These are the standard, worldwide Eiffel conventions and should be followed; experienced Eiffel programmers assume that the author has followed the conventions when they read someone else's code.
Great care should be taken to choose the correct name. Programmers spend most of their time reading other people's code, not writing new code. Most of the money spent in computing is spent on changing existing code, not on writing new code. The "software crisis" has arisen because much of the existing code cannot be understood or modified, and has to be either thrown away or re-written from scratch. Eiffel was designed to build reusable software and to help solve the software crisis, so you should always write your code for the next person, and remember that words are very useful in communicating between people.
Common error: two attributes have the same name, generating a name clash
Error code: VMFN
Error: two or more features have the same name
What to do: If they must indeed be different features, choose different names or use renaming.
Expressions are used to calculate a value; that value may be stored in a variable using an assignment statement, or the value may be used immediately in the code. An expression is a sequence of values connected by operators. The values may be variables, constants, or literals; a literal is an explicit value written as part of the code, such as 0.5 in the expression price * 0.5. The basic way to evaluate an expression is to evaluate the operators in left to right order, but there are two exceptions to this basic rule: operator precedence and explicit bracketing.
Each operator has a specific precedence or priorityor priorityor. When
a flat expression (an expression with no brackets) is evaluated, the operators
are evaluated first in precedence order, then in left to right order. The
precedence order for the INTEGER operators is shown below, from
highest to lowest precedence:
+ - unary plus, unary minusFor a flat expression, all exponents are evaluated before any multiplication is done, all multiplication is done before any addition, and so on. Operators at the same level of precedence are executed from left to right in the expression. Brackets are used to force evaluation, because expressions within brackets are evaluated before unbracketed ones.^ exponent
* / // \\ times, divide, divisor, modulus
+ - plus, minus
The rules that the computer uses to evaluate a complex expression are thus:
1. Select an expression in brackets, starting with the deepest brackets
2. In that expression, evaluate all operators at the top level of precedence, left to right
3. In that expression, repeat the evaluation for each level of operator precedence
4. When the flat expression is evaluated, find the next, most nested flat expression
5. Repeat 2-4 until the whole expression has been evaluated
Several example expressions, the order of evaluation, and their results,
are shown below. The complete Eiffel operator precedence order is given
in Appendix A.
1 + 2 * 3 ^ 3 - 4 / 5 (1 + 2 ^ (2 * 3)) / 4Brackets should be used to make the expression clear where there is any possibility of confusion. You should always write code with the next person in mind, who has to read and understand your code, and explicit brackets can make an expression clearer to understand.-> 1 + 2 * 27.0 - 4 / 5 -> (1 + 2 ^ 6) / 4
-> 1 + 54 - 4 / 5 -> (1 + 64.0) / 4
-> 1 + 54 - 0.8 -> 65 / 4
-> 55 - 0.8 = 16.25
= 54.2
-73 \\ 12 // 5 + 3 ^ 2 / 4
1 + 2 ^ 2 * 3 / 4 -> -73 \\ 12 // 5 + 9.0 / 4
-> 1 + 4.0 * 3 / 4 -> -1 // 5 + 9 / 4
-> 1 + 12 / 4 -> -1 + 9 / 4
-> 1 + 3.0 -> -1 + 2.25
= 4.0 = 1.25
A variable gets a value when it is created. This value may be changed
when the code is executed by an assignment statement. An assignment statement
or instruction has the form
identifier := expressionA value is produced when the expression on the right-hand side of this statement is executed or evaluated, and the value of the expression is then stored in the variable listed on the left hand side. The Eiffel convention for writing assignment statements is to place a space before and after the assignment operator.
Some examples of integer assignments are shown below:
total_cost := air_fare + bus_fare + hotelAssignment creates the data flow in a system. Data in a variable on the right hand side of an assignment (the source) "flows" into the variable on the left hand side of the assignment (the target). To illustrate how data values flow through the variables, a code fragment is shown to the left below and the values of the variables at each step are shown to the right.area := length * height
count := count + 1
A data flow diagram shows the data flow, illustrated in the centre below.
The values of length and height are used to calculate area,
and the values of area and width are used to calculate the
volume. A data flow chart shows the data flows in the system, not
the order of the operations. We could swap the order of the first two inputs
and still have a correct solution.
Assignment is a two step process that occurs in time. The assignment
count:= count + 1 is both correct and common; the variable count
is incremented by 1, so the value of the variable is larger by one,
after the statement has been executed. This is very different from an equality
test; it is impossible for a value to be larger than itself. The assignment
operator should thus be called something like assigns, gets,
or is. The operator should never be called equals; it does not test
for equality, it assigns a value to the variable.
If you write code that Eiffel cannot parse, you get an error messsage that describes
1. What the error is
2. Where the error is
3. How to fix the error
This is exactly the information you need to fix your mistake, so it is important to listen to what the Eiffel compiler tells you in the error message. Novices often ignore the content of an error message, however, possibly because it uses words with which they are not familiar.
Consider the simple class shown below:
class XThis code shows the most common novice error, to forget to declare a variable. You must declare a variable before it can be used; if not, Eiffel gives you the compile time error:creation
makefeature
a, b: INTEGER
make is
-- add two numbers together
do
sum := a + b
end -- makeend -- class X
Error code: VEEN
Error: unknown identifier
What to do: <make sure that the identifier is defined>
Class: X
Feature: make
Identifier: sum
The last three lines give the location of the error. In this case, Eiffel could not work out what to do with the identifier sum in the routine make in the class X. By reading the last three lines of the error message, you know the class, the routine, and often the line where the error was found.
The first two lines give the error code (ignore this), and a short description of the error. In this case, the error was that Eiffel ran across an unknown identifier sum in the make routine of class X, so it says "unknown identifier"). An identifier can be the name of a variable, a routine, or a class, so you need to work out which of these is the case, by looking at the name of the identifier on the last line of the error message.
The hardest part of the error message is the "What to do" section, because
an error message uses words that have a precise meaning in Eiffel, but
that you may not understand. Often the rest of the message provides all
the information you need to find and fix the error, as is the case here.
The "fix" given in the message above (enclosed in < >) is not even close
to the Eiffel error message (run the code to see the message), but the
rest of the message is so good you do not need to understand the "What
to do" message.
The word "input" is used here to describe the act of typing data into the computer via the keyboard, and "output" is used to describe data that is shown on the terminal screen. Input and output operations are controlled in Eiffel by a special system object whose name is io, so every input and output command uses the object io, and has the form io.feature. Because Eiffel is a fully OO language, it uses a different command for each type of input and output.
To get a single, integer value from the user requires three lines of
code. The first line is a prompt, that writes a string to the terminal
screen so the user knows what to do. The second line is a command that
reads the value that the user types into the keyboard. The third line takes
this value and stores it in a variable in your system. An example of this
input code is
io.putstring ("Please enter your age in years: ")This command gets a value from the keyboard and stores that value in the Eiffel integer buffer; a buffer is a temporary storage area in the computer. Each input type in Eiffel has its on buffer. After Eiffel executes this instruction, the value from the keyboard has been read and stored, and your Eiffel system can now use that value.io.readint
age := io.lastint
To read an integer, you use a command of the form
io.readint
To find out what the entered value was, you ask for the last integer read in using the query
io.lastintThis function returns the last integer value that was read. Because it is a function, it can be called many times and will return the same value each time; by definition, a function changes nothing.
Every input from the user should be preceded by a prompt, so the user knows what to type. You don't need to use quotes to input values; the computer finds out how to treat the input by looking at the type given in your input command. Note that the input command io.readint does not mention a variable. Eiffel stores its input in a buffer, one buffer for each type of object. To access the buffer, you issue a query to get the last value read into the buffer. Getting the last value does not change the value of the buffer, it only reads that value, so you can get the last value as many times as you want. The input will only change when you read in a new value for that type of object. Reading in a new value changes the value stored in the buffer, so any input value must be stored used by your system before the next value is read in from the user.
To output an integer, you write a label to explain the output
to the user, followed by an output command. Few things are as puzzling
as a number showing on the screen with no explanation of what the value
means. A label has the same form as a prompt (it is a string shown on the
screen), so the code needed to output an integer is
io.putstring ("Your age in years is now ")An output is a command, because it changes the state of the screen. The output command, the type it requires, and a sample screen output for the command, areio.putint (age)
io.putint (x) x: INTEGER 12Each input command reads a single value, and each output command displays a single value; if you need to read or display several values, then you have to use a separate command each time. Output of a label and three integers thus needs four lines of code. At least one space should be placed between a prompt and its input value, and between a label and its ouput value.
Prompts and labels are examples of STRING output, where a string
is displayed on the screen to communicate with the user. String output
uses the command io.putstring and takes a single argument, the string
to be displayed. A string is enclosed in double quotes. A long string may
be written on two lines by starting every line but the first with a '%'
and ending every line but the last with a '%'. Any number of blanks or
tabs may be placed between the '%' signs with no effect on the format of
the output. An example of a long string output is
io.putstring ("Having a wonderful time.%This command produces the screen output% Wish you were here.%
% Please send money")
Having a wonderful time. Wish you were here. Please send money.
A new line may be started on the terminal screen by issuing the command
io.new_line; this forces the next output to start at the beginning
of the next line on the screen. If the next line output is a string, the
two lines can be combined by using the new line indicator %N. The following
two pieces of code have the same behaviour:
io.new_lineIn the same way, a tab can be written to screen using the special character %T; a character is enclosed in single quotes, where a string needs double quotes. Special characters are written as two symbols, the special symbol '%' and a following symbol; the new line character is denoted by '%N', the tab character by %T, and the percent character by %%. The complete list of special characters is shown in Appendix B. An unprintable character can be specified as an ASCII value using the special character form '%/code/', where the code is the ASCII (decimal) value of the character. A new line character, for example, has an ASCII code of 13, so it can also be specified as '%/13/'.io.putstring ("Gday")
io.putstring ("%NGday")
When an integer is displayed on the screen, Eiffel shows all the digits in the integer, so the number of characters shown on the screen changes with the value of the number. If tabs are used to make the output appear in columns, then each number has to be shown in a fixed length field, so the columns line up on the screen.
The class FORMAT_INTEGER is used to produce fixed length integer
output by changing the integer into a fixed length string, and then displaying
the string on the screen. An object of this class is created and given
a field width as an argument. The feature formatted of this object
is then used to print integers with width number of characters.
The basic way to format integers for output is shown below, using a field
width of 8:
class XThis code looks complex, but is easy to adapt as needed. To display in a different field size, all you need to change is the field width when the object format is created, in line 1 of the make routine here. To display a new variable, all you need to do is to write the name of that variable in brackets after the word formatted; this is the argument to formatted. The feature formatted takes the integer, converts it to a string of size 8 (here) by adding leading blanks if needed, and returns this string value. The string is then output using the normal io.putstring command. If the number of digits is larger than the field width, Eiffel takes as many characters as needed to print all the digits in the value. The class FORMAT_INTEGER has many other formatting features to offer; to see these features, look at the class listing in /opt/Eiffel3/base/support or run short on the class (see Section 5.9).creation
makefeature
test: INTEGER
format: FORMAT_INTEGERmake is
-- show how integer formatting is done
do
!!format.make (8) -- create formatting object
test := 43
io.new_line -- start in column 1 of screen
io.putstring ("Value:%T") -- label and tab
io.putstring (format.formatted (test)) -- display in width eight
end -- makeend -- class X
A real number is a number with both an integer and a fraction part,
and is implemented by class REAL in Eiffel. In Eiffel at SoCS, the
integer part of a real number can take on a value between about -10^39
and +10^39, and a fraction value from 0 to about 10^-39.
Declaration of REAL variables is similar to INTEGER declarations,
for both variables and constants. A real number is given the default value
0.0. Examples of REAL declarations are shown below, followed by
their data structures;
A REAL constant is written with a decimal point and a fraction; if you simply write an integer value here, Eiffel will report a type mismatch error.
The numeric operations defined on real numbers are similar to those
for integers, except that mod and div are only defined for
integer values. The REAL operators are shown in their precedence
order below:
+ unary plus +6.2Now that we have two numeric types, we need to define how their values are used in an expression. Any numeric value in an expression is first converted into the "heaviest" type in the expression, then the expression is evaluated. A REAL value is heavier than an INTEGER value. If two INTEGERs are added, the result is an INTEGER; if an INTEGER and a REAL number are added then the result is of type REAL; minus, times and divide behave in the same way. Any number of operators can be combined within a single expression.- unary minus -42.7
^ exponent 3.6 ^ 2.4
* times hours * rate
/ divide total / people
+ binary plus 3.7 + total_cost
- binary minus total_hours - overtime_hours
Because an operator is defined on a type, the effect of the operator can differ depending on the type of its arguments; this technique is called overloading. We have already seen an overloaded operator, the operator make. This operator has the same name in each class, but does different things depending on the code defined in the class. The Eiffel class INTEGER contains the integer operators, and the Eiffel class REAL contains the real operators. Each class contains operators named "+", "-", "*", "/", and "^"; which operator is actually called depends on the type of the arguments. If the arguments are both integers, then Eiffel uses the integer operators. If at least one of the arguments is of type REAL, then the real operators are used. The signatures of the real operators are shown below; note that for the binary operators, if one argument is of type REAL then the other is converted to type REAL before the result is calculated.
symbol name use result
+ unary plus REAL REALThe most common novice error at this point is to use incompatible types; the two sides of an assignment statement, for example, must have compatible types. Two types are compatible if they are the same type, or one can be converted to, and thus stored in, the other. For the numeric types, an INTEGER value can be stored in a REAL variable, but a REAL value cannot be stored in an INTEGER variable.- unary minus REAL REAL
^ exponent REAL, REAL DOUBLE
* times REAL, REAL REAL
/ divide REAL, REAL REAL
+ binary plus REAL REAL
- binary minus REAL REAL
The code given below produces the following error message:
featureError code: VJAR
a, b: REAL
sum: INTEGER
...
sum := a + b
Error: source of assignment does not conform to target
What to do: make sure that type of source (right hand side) conforms
to type of target
Class: XEiffel tells you where the error is located: in class X, in routine make, in the identifier sum. The fix part of the message then refers to the source and target of an assignment statement. The source is where the value comes from; the right hand side. The target is where the value goes; the left hand side. The type of the source is REAL, of the target INTEGER, so of course they are incompatible; you cant store a real number in an integer. All of this information makes your task very simple; you need to simply declare sum to be REAL, and the types of the assignment statement are then compatible. The only tricky part of the message is the word "conform"; informally, "conform" means that two variables either have the same type, or are compatible.Feature: make
Target name: sum
Target type: INTEGER
Source type: <REAL>
io.readreal to get the value from the keyboard and store it in the REAL buffer, then
io.lastreal to get the value from the buffer and store it in a variable.
To output a real number, we use
io.putreal to display the value on the screen.
An example of REAL input and output is shown below:
featureWhen Eiffel executes the input command io.readreal, it reads a value from the screen and stores it as a real value in the REAL value buffer. You do not have to type in a decimal part if the decimal part is zero, because you have told Eiffel that the input is a real number and it will automatically add a decimal part to the input value if none is explicitly given on the screen. An input value of 3, for example, is stored as the real value 3.0.
test: REAL
make is
-- illustrate REAL input and output
do
io.putstring ("Enter a number: ")
io.readreal
test := io.lastreal
io.putstring ("%NThe value was ")
io.putreal (test)
end -- make
When Eiffel executes the output command io.putreal, it displays
as many digits as needed to show the value of the number. If the decimal
part is zero, then only the integer part is displayed. If the decimal part
has only two digits, then only two decimal digits are displayed. Several
examples of real values and their screen output via io.putreal are
shown below:
test := 3.0 io.putreal (test) 3The command io.putreal expects a real value, and that is the only constraint on io.putreal. The value could be a real literal, a real variable, a real constant, a real expression, or a real function. All that matters to io.putreal is that the value it is given as an argument is a real value; several examples are shown below:test := 3.1 io.putreal (test) 3.1
test := 3.12345 io.putreal (test) 3.12345
io.putreal (3.1) 3.1The output can be formatted nicely by converting a number to a string, and then printing the string; the features to do this are supplied by class FORMAT_DOUBLE (there is no class FORMAT_REAL). An object of type FORMAT_DOUBLE is created and used to control the ouput via a format specifier. The format specifier for real numbers gives an output field width and the number of decimal places, so the format specifier has the form (width, precision). The width is the total length of the string, with precision decimal places. A correct output value is always returned, so Eiffel takes as large a field as needed to print the integer part of the value, and uses the specified precision for the decimal part. If the number is too small, leading spaces are added before the output value.test := 3.1
io.putreal (test) 3.1
test := 3.0
io.putreal (test + 0.1) 3.1
io.putreal (sqrt (3.0)) 1.73205
Code to output a field of total length 8, with two decimal places, is
shown below. The value displayed on the screen will be eight characters
long: three spaces, followed by the two digit integer part, then the decimal
point, then two digits for the decimal part.
class XThe class offers many other formatting features.creation
makefeature
test: REAL
format: FORMAT_DOUBLEmake is
-- show how real formatting is done
do
!!format.make (8, 2) -- create formatting object
test := 43.789
io.new_line -- start in column 1 of screen
io.putstring ("Value:%T") -- label and tab
io.putstring (format.formatted (test)) -- display in width eight
end -- makeend -- class X
A double precision real number is implemented by class DOUBLE in Eiffel. This type of number has the same range of values as a REAL number for the integer part, and can store more precise values in the fraction part of the number. The integer part of a DOUBLE number in Eiffel at SoCS can vary from about -10^39 and +10^39, and the fraction part can have a value from 0 to about 10^-49.
A double variable has a default value of 0.0. Three declarations of
type DOUBLE are shown below, with the data structures of the variables.
The value is shown with elipses ("...") to indicate the extra precision
of the fraction part.
Input is done using the command io.readdouble and the function
io.lastdouble. Output is done with the command io.putdouble.
Output formatting can be controlled by features of the class FORMAT_DOUBLE.
An example of double precision input and output is
featureThe signature of each DOUBLE operator is shown below:
test: DOUBLEmake is
-- illustrate DOUBLE input and output
do
io.putstring ("Enter a number: ")
io.readdouble
test := io.lastdouble
io.putstring ("%NThe value was ")
io.putdouble (test)
end -- make
symbol name use result
+ unary plus DOUBLE DOUBLEIn summary, the exponent operator always returns a value of type DOUBLE while the other operators may return a value of type INTEGER, REAL, or DOUBLE due to operator overloading.- unary minus DOUBLE DOUBLE
^ exponent DOUBLE, DOUBLE DOUBLE
* times DOUBLE, DOUBLE DOUBLE
/ divide DOUBLE, DOUBLE DOUBLE
+ binary plus DOUBLE DOUBLE
- binary minus DOUBLE DOUBLE
The class REAL supplies the basic real number opersations to
do arithmetic, but there are also many other useful, but complex, operations
we can do on real numbers. Sophisticated mathematical functions such as
sqrt and sin are provided by the Eiffel class SINGLE_MATH
. This class is inherited by the class that wants to use its features;
to see the features in the class, you can ask for a short or a flat
display by typing
short SINGLE_MATH, orThe short tool shows you all the features that are defined in a class. The flat tool shows you all the features that are offered by a class. A feature may be immediate (defined in the class) or inherited; inheritance is discussed in Chapter 10.flat SINGLE_MATH
To find the square root of a real number, for example, we use the class
SINGLE_MATH with the code shown below:
class XInheritance is discussed in detail in Chapter 10, but for now all we need to know is to add the keyword inherit and the class to inherit (SINGLE_MATH here) after the class and before the creation clause. When this is done, all features of the class (such as sqrt here) can be used within the class X. The feature sqrt receives a single argument in brackets (the number to use) and returns a real value that is the square root of the argument. This real value is then displayed using the output command io.putreal.inherit
SINGLE_MATHcreation
makefeature
test: REAL is 3.0make is
-- show how to use the inherited function sqrt
do
io.putstring ("The square root of the test value 3.0 is ")
io.putreal (sqrt (test))
end -- makeend -- class X
Conversion between numeric types is provided by operators in the REAL and DOUBLE classes (for heavy to light conversions), or automatically by the compiler (for light to heavy conversions). Note that the value is not changed; instead, a function is called on the value and the function returns a new value; the existing value is unaltered. The name and signature of the conversion operators are shown below.
operator name argument result example
truncated_to_real DOUBLE REAL d.truncated_to_real
truncated_to_integer DOUBLE INTEGER d.truncated_to_integer
truncated_to_integer REAL INTEGER r.truncated_to_integer
It is also possible to use these operators with expressions or functions by wrapping brackets around the expression, such as
(3.6 + sqrt (2)).truncated_to_integer => 5
A larger example of type conversion is
class XThe numeric classes provide many more features than are used here; you can see all the features in a class by running short or flat on that class.creation
makefeature
test: REAL is 3.12345make is
-- show how to truncate a real value
do
io.putstring ("The integer part of the test value")
io.putreal (test)
io.putstring (" is ")
io.putint (test.truncated_to_integer)
end -- makeend -- class X
A printable character is a character with a value in the set 'a' to 'z', 'A' to 'Z', '0' to '9' as well as punctuation marks ('!', '.', ',', ';', ':', '?'), and logical ('<', '=', '>') and arithmetic ('+', '-', '*', '/') symbols. There are also other, less common printable values such as '@', , '#', '$', '%' and so on. A printable character is written in Eiffel enclosed in single brackets.
A character may also have an unprintable value, such as the new line character that is named CR (Carriage Return), the backspace character (named BS), the end of transmission character (named EOT), and even the character that was used to ring the bell on a teletype (named BEL)! The common unprintable, but useful, characters are shown in Appendix B.2.
The full character set is the standard ASCII (American Standard Code for Information Interchange) character set of 128 characters. A lexical order is defined on the set, based on the ASCII value of each character. For the common printable characters, '0' < '1' < ... < '9' < 'A' < 'B' < ... < 'Z' < 'a' < 'b' < ... < 'z'.
A character is declared like any variable. A variable of type CHARACTER
is given an initial, default value of NUL, written as , and named the
null character. Several declarations of type CHARACTER are shown
below, with the resulting data structures:
A character is input in the normal way, by a procedure (io.readchar) to read the keyboard and store the value in a character buffer, followed by a function (io.lastchar) to return the value of that buffer. As always, every input should be preceded by a prompt to tell the user what to do. Character input thus uses the following basic method:
choice: CHARACTER
...
io.putstring ("Enter your menu choice: ")
io.readchar
choice := io.lastchar
Care must be taken with character input, because most of the time
your system wants one character but the user actually types in two characters:
the printable character and then the new line character CR. Consider the
following wrong code:
account, choice: CHARACTERWhen the user sees the first prompt, they type in an account identifier, such as 'S' for a savings account and 'C' for a credit account; the quote marks are not actually typed into the keyboard, they are simply used here for clarity. The user then hits the Return key on the keyboard, and the command io.readchar takes the account choice character and stores it in the character buffer, then io.lastchar gets the value from the buffer and returns it so it can be assigned to the variable account. The second io.readchar then reads the next input character, which was a CR!...
io.putstring ("Enter your account id (S, C): ")
io.readchar
account := io.lastchar
io.putstring ("Enter your choice for this account (D, W, S): ")
io.readchar
choice := io.lastchar
To overcome this problem, you need to "flush" the unwanted CR from the
system. One way to do this would be with a dummy read, but this is an ugly
solution; we really don't want to read and then throw away the CR. Instead,
we can tell the computer to start reading input from the next input line,
after the CR; this is done with the command io.next_line. The correct
way to read a single character from a line is thus
io.putstring ("Enter your account id (S, C): ")that throws away the CR. The next input is then read from the start of the next line.
io.readchar
account := io.lastchar
io.next_line
Character output is implemented in the normal way by a command io.putchar.
A literal value can be output, such as io.putchar ('D'), but more
often a character variable is output. Here is the code to display the user's
account choice on the screen:
io.putstring ("Your account choice was ")Declaration, input, assignment, and output have now been covered for the four basic Eiffel types INTEGER, REAL, DOUBLE, and CHARACTER. The remaining basic type, BOOLEAN, is covered in Chapter Five.io.putchar (choice)
The problem specification is
"Money is deposited into and withdrawn from a bank account, and the balance can be displayed. Interest is added daily on the current balance; the interest rate is 4.5% a year."
An input command io.read<type> stores an input value in a buffer for each type of object. The last value of that type read from the user is returned from the query io.last<type>.
An output command of the form io.put<type> shows a data value of that type.
An assignment statement evaluates the expression on the right-hand side, and stores the value in the variable on the left-hand side
Operators have a strictly defined precedence order, but this precedence order can be overridden by parentheses
Operators are defined on types, so an operator can have different effects by overloading.
The basic type CHARACTER includes both printable and unprintable
characters; the carriage return character CR, for example, is a valid character.
1. How is an attribute declared? How is a constant declared? What is the difference betwen a literal and a constant? Can an attribute have the same name as a class?
2. What is the default or initial value of an attribute?
3. What are the names of the three numeric types in Eiffel? Is there a class for each type? Is there a text file for each type? How can you get a list of the operators defined for each type?
4. Write down a command to read in:-
an integer
a real number
a double precision real number
a character
5. An input command gets a value from the user, and stores it in a buffer. How do you get the value from the buffer? How many instructions are there to get a value from an input buffer?
6. Describe the general method for getting a value from the user. Write the declarations and code needed to read and store:-
two integers
two real numbers
an integer and a real number
7. Write down the command to output:-
an integer
a real number
a double precision real number
a character
8. When do you need a prompt? a label?
9. Explain, step by step, how an assignment statement works.
10. What is meant by operator precedence? What is the numeric operator precedence order?
11. Evaluate the following expressions, showing each step:
1 + 2 * 3 / 4.012. Write a class X that consists of a single make routine, plus attributes. Write a make routine to read in the weight of an object in pounds, convert the weight to kilograms, and show the answer. The program should print out both the weight in pounds and in kilograms. One pound is equal to 0.453592 kilograms. 1 // 2
34 // 4.5
1 // 2 \\ 3
1 \\ 2 * 3 // 4 / 5.0
-43 // 4 ^ 2
-((12 / 3.0) * (0 // 7) + 2)
13. Write a class X that consists of a single make routine, plus attributes. Write a make routine that reads in an employee's hourly rate, the number of hours worked, and the tax rate. It then finds and shows the gross salary (before tax) and net salary (after tax) for the employee.
14. Write a class X that consists of a single make routine, plus attributes. Write a make routine that converts degrees in fahrenheit to degrees in centigrade. Fahrenheit degrees range from 32 degrees F (freezing point of water) to 212 degrees F (boiling point of water), Centigrade degrees range from 0 (freezing) to 100 (boiling).
15. Write a class X that consists of a single make routine, plus attributes. Write a make routine that finds the time and cost of a car trip. The input data is the distance covered on the trip, the average speed, the number of litres of petrol used per hundred kilometres, and the cost of a litre of petrol.
16. Write a class X that consists of a single make routine, plus attributes. The specification is:
"You have decided to become a rock concert entrepreneur and want to use your knowledge of computing to help with the accounting. Write a system that calculates your individual profit and the total attendance for a rock concert.
There are three ticket prices, the cheap seats at $10, the standard seats at $20, and the special seats for $100 each. The special ticket holders get to sit in the front row, plus a pair of autographed sunglasses, plus a chance at a backstage pass. You must pay for the rent on the stadium, cost of the band, security and insurance. The security is calculated at 32 cents per person attending. The insurance is 3.6% of the income.
You have two partners in this venture, and must split the profits evenly between all three partners. You must pay 12.5% tax on any profits made from the concert. Show the net (after tax) profit per person, and the total attendance at the concert."