Numbers up.

 

            Hopefully by now you’re feeling pretty confident with puts and for statements.  While it’s important to be able to work with strings, computers are best at working with numbers.  If you think back to your junior high math classes, you’ll remember (or maybe not) some discussions on different types of numbers: real numbers, rational numbers, integers, whole numbers, and imaginary numbers.  In programming, we’ve given slightly different names to these different types of numbers.  Real and rational numbers are called floating point.  Both whole numbers and integers are called integer.  In other languages, we also have to worry about how much memory each type of number uses.  In C, a short integer uses 1 byte and can only be between –128 and 127.  If the number is unsigned, it can be between 0 and 255.  An integer is 2 bytes, and a long integer is 4 bytes.  Normal floating point numbers are 2 bytes, and double-precision floating point numbers are 4 bytes.  When variables are first created in these languages, you must first decide what type of number you plan to use.  Then you need to use special statements that only work with that type of number, or use a special conversion statement to make it work. 

 

            In Euphoria, all of these numbers can be safely defined as atom.  An atom in Euphoria is 4 bytes.  One bit is used to define whether or not the variable part of a sequence or atom, so that means an atom can be any number between –1073741824 and +1073741823.  Floating point numbers are accurate to 14 decimal places.  If you need to work with larger numbers, you should look for one of the big number libraries on the “Recent User Contributions” page of the Euphoria website.  “Big Number Arithmetic” by Craig Gilbert has been around for quite a while, and “Big Math” by Matthew Lewis boasts a great many features.

 

            Let’s do some number crunching of our own in a program named numbers1.ex.  Type, save, and run the following program:

 

constant TAXRATE = 0.07

 

atom aftertax

 

printf(1,”The tax rate is %5.2f%%.\n\n”,

       TAXRATE * 100)

for dollar = 1 to 20 do

  aftertax = dollar * TAXRATE

  printf(1,”The tax on $%2d is $%04.2f.\n”,

         {dollar,aftertax})

end for

 

            If you entered the program correctly, you should have seen a line with the tax rate, and 20 lines with the tax calculated.  The first new thing in the program is the use of the constant.  A constant is not a new data type like atom or sequence, but it is a reference to an unchangable piece of data.  Constants are always initialized in the definition, while variables are never initialized in the definition.  You’ll also notice that we entered the constant name in all capitals.  This is a common practice when naming constants to make it clear to anyone reading page 500 that the name defined on page 1 is a constant.  It is not necessary to, nor necessarily poor style if you don’t, capitalize your constants; but it does make it easier in many programs to differentiate. 

 

            There are three main reasons for us to use a constant instead of a variable.  First, it prevents the value from changing during the course of the program.  Second, it allows us to change the value in a single line rather than searching through thousands of lines to change each the value each time it’s used.  Third, it makes it easier to tell what the program is doing if the constant is clearly named.

 

            The next new statement is printf.  This statement comes directly from C and has almost the exact same syntax as its C counterpart.  There are three parameters in printf: the file pointer, a format string, and the data.  As we mentioned with puts, we use file pointer 1 to print to the screen.  Remember that puts can only print strings.  We use formatting codes in the printf string to tell Euphoria what the number is supposed to look like.  These format codes always begin with a percent sign.  The following explanation of format codes is taken directly from the Euphoria manual (library.htm):

%d - print an atom as a decimal integer
%x - print an atom as a hexadecimal integer
%o - print an atom as an octal integer
%s - print a sequence as a string of characters, or print an atom as a single character
%e - print an atom as a floating point number with exponential notation
%f - print an atom as a floating-point number with a decimal point but no exponent
%g - print an atom as a floating point number using either the %f or %e format, whichever seems more appropriate
%% - print the '%' character itself

Field widths can be added to the basic formats, e.g. %5d, %8.2f, %10.4s. The number before the decimal point is the minimum field width to be used. The number after the decimal point is the precision to be used.

If the field width is negative, e.g. %-5d then the value will be left-justified within the field. Normally it will be right-justified. If the field width starts with a leading 0, e.g. %08d then leading zeros will be supplied to fill up the field. If the field width starts with a '+' e.g. %+7d then a plus sign will be printed for positive values.

            Based on this, we can see that the first line is supposed to print the tax rate as a percent (including the percent sign) using a maximum of five characters and two decimal places.  The five characters come from 2 digits before the decimal plus the decimal itself (1 character) plus the 2 decimal places.  Since we didn’t include a zero before the 5, and we didn’t include a minus sign before the 5, the value will be printed with a single leading space since we only need 1 digit before the decimal.

 

            We only need one item of data in this statement.  Since TAXRATE is a decimal and we want to print it as a percent, we need to use an expression to convert it.  Very simply, we multiply the decimal by 100 to get the percentage value.  However, if you’ve never programmed before, you might expect the letter ‘x’ to represent multiplication.  Sorry, folks, it doesn’t.  Addition and subtraction are represented by plus and minus (+ and -) as you would expect, but multiplication is represented by the asterisk (*) and division by the slash (/).

 

            The next expression we see is inside the for loop.  This expression calculates the tax on the current dollar amount and assigns it to our variable aftertax.  Because we want to line up our results, we’ll again use printf to display them.  We want our dollar amount to be displayed as an integer, so we use %d allowing two places.  Since want the tax to be displayed with two decimal places, we choose %f as our format code.

 

            Take special note of the data parameter in our second printf.  Notice the curly braces ({}) that surround our two variables.  This tells us the parameter is a sequence instead of an atom.  Remember from last chapter that we said a sequence is a list of objects.  When we used strings, we used quotations marks to deliniate the sequence because we were referring to a list of characters.  When we’re referring to a list of numbers, or a list of mixed objects, we need to use the curly braces to show the sequence.

 

            We’ve covered quite a lot of material in this short program.  Take some time now to play with it a little bit.  Change the format codes and see what happens.  Change the value of TAXRATE and see how easily the program adjusts itself.

 

Playing with files

 

            We’re going to start getting a little more complicated now.  Don’t worry – if you’ve managed to keep up this far, you’ve already crossed the hardest barriers.  Type in this next program, save it as number2.ex, and run it.

 

constant CUSTNUM = 174          -- Customer number

constant ITEM1 = "Coat"         -- First item purchased

constant ITEM2 = "Umbrella"     -- Second item purchased

constant PRICE1 = 24.99         -- Price of first item

constant PRICE2 = 8.95          -- Price of second item

 

sequence filename       -- Name of output file

atom fp                 -- File pointer for output file

 

sequence today          -- Current date & time information

atom total              -- Total payment due

 

filename = sprintf("i%07d",CUSTNUM)&".txt"

fp = open(filename,"w")

 

today = date()

printf(fp,"Invoice for customer %d on %2d/%2d/%4d at

       %2d:%02d:\n\n", {CUSTNUM,today[2],today[3],

       today[1]+1900,today[4],today[5]})

printf(fp,"%-15s: $%6.2f\n",{ITEM1,PRICE1})

printf(fp,"%-15s: $%6.2f\n",{ITEM2,PRICE2})

total = PRICE1 + PRICE2

printf(fp,"%-15s: $%6.2f\n",{"TOTAL",total})

close(fp)

 

puts(1,"Invoice written to file: " & filename & "\n")

 

            By now the concept of constants should be pretty clear.  You’ll notice that since we’re keeping track of a lot more information than in previous programs, we’re using more comments to keep things clear.  Clear variable names by themselves aren’t going to cut it in a larger program.  Notice also the use of white space to separate major sections.  Proper use of white space in indentation is just as important to good style as good comments and clear variable names. 

 

            We have two blocks of variable definitions.  The first block is for our file operations, the second is for use in printing the invoice.  We repeat the same block separation with our statements.  Take a look at how we create our filename.  We could use a string literal, which would be perfectly acceptable.  However, in this case, we are using the customer number (CUSTNUM) to create the file name so we can have a unique invoice file for each customer.  To do this, we will use the sprintf function.  A function is a statement that returns a value.  We use functions in the same places that we would use variables or constants.  In contrast, statements like printf and puts are called procedures.  Procedures must be used by themselves at the beginning of a line, where functions cannot be used by themselves at the beginning of a line.

 

            The sprintf function is exactly like the printf procedure except for two things:  sprintf must be used as part of an assignment or part of another procedure, and sprintf does not use a file pointer.  That means there are only two parameters to sprintf: the format string and data.  In creating the file name, we are using the letter ‘i’ to mean “Invoice”, a 7-digit customer number with leading zeroes, and an extension.  We could have easily included the extension as part of the format string, or used concatenation to join the letter ‘i’ with the customer number; but this method shows clearly the break between filename and extension.

 

            The next line opens our file for writing.  It may surprise you that open is a function rather than a procedure.  This is because it is possible for file operations to fail.  The most likely places for file operations to fail are when opening the file and when reading from the file.  Therefore, these operations are functions.  In a true user-friendly program, we would test the success of open immediately after the function; however, that is beyond the scope of this chapter.  We’ll get to that a couple of chapters from now.  In the meantime, if there is a problem, the program will stop with an error message.

 

            The second parameter of open may also be confusing.  The second parameter must be a string with one of the following meanings:

 

“w”  -  open the file for writing text,

        overwriting any existing file

“wb” -  open the file for writing binary,

        overwriting any existing file

“r”  -  open the file for reading text

“rb” -  open the file for reading binary

“a”  -  open the file for writing, appending to

        any existing file

“u”  -  open the file for both reading and

        writing

 

            If you try to read from a file that is opened in write mode or vice versa, your program will crash with an error.  So why not open every file in update mode?  First, if the file doesn’t exist, it will generate an error.  Second, you can easily overwrite existing data in the middle of the file and corrupt the whole thing.  That would not be good.  We’ll discuss reading and updating more in the next chapter when we talk about input.  For now, let’s get back to writing our output to a file.

 

            The statements printf and sprintf lend themselves very nicely to printing out date and time information.  Euphoria has a nice selection of built-in functions, which can all be found in “library.htm” of the Euphoria manual.  One of those is date, which returns, if you can’t guess, the current date and time.  The date function takes no parameters, but the parentheses are still needed.  It returns a sequence of eight elements.  In Euphoria, as with many other languages, we reference each element by using straight brackets ([]) and an index number, starting with 1.  Each element returned by date is an atom.  The sequence returned is this: {year, month, day, hour, minute, second, day of week, day of year}.  The only unintuitive element is year, which is actually the number of years since 1900.  To get the current year, we need to add 1900 to today[1]. 

 

            In order to get the lines to print to the file instead of the screen, we simply use our fp variable instead of 1.  The same thing works for puts or any other statement that displays information on the screen.  In this program, we want to line up the numbers even though the descriptions (ITEM1, ITEM2, and total) are of different lengths.  We can use the format code %s to handle this.  We used the minus sign to left justify the text, and we allowed for 15 characters.  If you had played around with the first program, you might have noticed that data longer that the space allows simply expands the string so it fits.  The same thing will happen here, which will push our numbers out of alignment.  Another way of aligning numbers might look something like this:

 

puts(fp,ITEM1)

for dot = length(ITEM1)+1 to 15 do

  puts(fp,”.”)

end for

printf(fp,”: $%6.2f\n”,PRICE1)

 

            Try inserting these lines just before the line that prints ITEM1 and PRICE1, and commenting out the full printf line (by adding two hyphens to the beginning of the line).  Re-run the program and look at the results in the output file.  Notice now that the spaces have been replaced by dots.  Also notice that it took 5 lines and a loop to do the same thing that was done in one line.  This is obviously not a very concise solution.  Since we can figure out how many dots we need by taking the difference between length( stringname ) and 15 spaces in this case.  The length function tells us how long any sequence is, not just a string.  Let’s use a more concise solution by replacing the above 5 lines with the following one:

 

puts(fp,ITEM1 & repeat(‘.’,15-length(ITEM1)) &

     sprintf(“: $%6.2f\n”,PRICE1))

 

            By using a combination of concatenation, repeat, length, and sprintf, we’ve now been able to create a clear, concise, and correct means of aligning our numbers using dots as fillers instead of spaces.  The repeat function takes two parameters: the object to repeat, and the number of times to repeat it.  It then returns the sequence we asked for.  Notice that we used single-quotes around the dot.  That is how Euphoria tells the difference between a character (an atom) and a string (a sequence).  If you had used double-quotes, you would have gotten an error saying something about a sequence inside a string.

 

            At the end of the program, we do a little cleaning up.  Before ending a program, you should always close any open files.  The close statement takes only the file pointer variable as a parameter.  Be careful not to use the file name as you will get an error.  In most languages, failure to close files before ending the program will result in data being lost and file corruption.  However, Euphoria will be kind and close any files you forget for you.  Even though this safety feature is built in, it is still no excuse for sloppy programming.  Make sure you always close all files before ending your program.

 

            The last bit of clean up we do is print a message on the screen telling the user where the output is located.  Even though the program would do everything we wanted without this message, the user would be left with a blank screen or an immediate return to the command prompt.  It would almost appear that the program didn’t do anything.  This simple message informs the user that the operation completed successfully.  The “no news is good news” philosophy may be appropriate in some applications; but that is rarely the case in programs run by end users.

 

            There is one last function I’d like to discuss.  Let’s change the date that’s printed to a two-digit date.  Change the line that prints the header to the following:

 

printf(fp,”Invoice for customer %d on “ &    

   “%d/%2d/%02d at %2d:%02d:\n\n”,

   {CUSTNUM,today[2],today[3],remainder(

   today[1],100),remainder(today[4],12),

   today[5]})

 

            If you’ve been following, you’ll notice that it’s quite possible to have one statement spanning several lines.  The only caveat with that is that strings cannot span to the next line.  If I need to break a string into several lines as in this example, I need to close the string and concatenate it to a new string on the next line.  Failing to do that will generate a syntax error.  Otherwise, I can break a statement at any punctuation mark (usually a comma or parenthesis). 

 

            The first thing I did in this statement was remove the leading space from the date.  Whether the month has one digit or two, the date will only have one space separating it from the word “on”.  The next thing I did was change the year from 4 digits to 2 digits and zero-fill it.  I’m not zero-filling the day, because it’s perfectly natural to space-fill the month and day, but not so the year.  The remainder function returns the remainder after dividing the first parameter by the second.  We applied the remainder of year / 100 to get our 2-digit year, and we applied the remainder of hour / 12 to get a normal 12-hour time.  If we were really adventurous, we could use a sequence containing {“am”,”pm”}, a %s format string, and a data element of ampm[floor(today[4]/12)+1] to get our time of day text to print after the time.  I leave it as an exercise for the reader to figure out how to fit these pieces into our program ;-). 

 

Review.

 

            We’ve been through quite a lot this chapter.  We’ve covered different types of numbers, constants, the printf and sprintf statements with their format strings, writing to files, sequences, and a number of neat functions to help us get the job done faster.

 

            Keep the numbers2.ex file handy.  We’ll be building onto it in the next chapter.  My challenge to you at the end of this chapter is to look in the Euphoria manual and study some of the math and miscellaneous functions available.  Don’t worry if you can’t figure them all out, we’ll be covering a lot more of them in later chapters.  If you’re looking for some direction, try printing a list of birthdays or appointments.  Or print a list of perfect squares.  Or sines of a series of angles. Or….

 

 

 

Next chapter….Need Input…