Rapid Euphoria 3.1.1

If you want to get the most out of the Euphoria programming language, getting a handle on how to work with data objects should be at the top of your list. There are a lot of library routines that are meant for handling data objects. You can check for the type of a data object. You can also change the way a data object is presented, such as going from 2.34 to "2.34", and can even go beyond the simple operators of Euphoria to create your own unique data structures.

Because some of the library routines in Euphoria can return either an atom or a sequence, it is very important to test for the type of a data object. The library routines involved are named the same as the variable type portion of a variable declaration statement. To begin, atom() returns a 1 if the data object is an atom value:

ri = atom(o)

ri = integer(o)

ri = sequence(o)

All of these library routines will return 0 if data object o is not the expected type. Run a demo from this screen now to demonstrate how to test for the type of a keyed-in data value. For obvious reasons, there is no such thing as an

include get.e atom atom_received, integer_received, sequence_received sequence inputted_string atom_received = 'n' integer_received = 'n' sequence_received = 'n' puts(1,"Enter any numeric value (such as 5.004 or -7)\n") while atom_received = 'n' and integer_received = 'n' do inputted_string = get(0) if inputted_string[1] = 0 then if integer(inputted_string[2]) then integer_received = 'y' puts(1,"\nThank you! This value is an integer!\n\n") else if atom(inputted_string[2]) then atom_received = 'y' puts(1,"\nThank you! This value is an atom!\n\n") end if end if end if end while puts(1,"Enter a sequence value (such as {54,-8,2.3} or \"Hi There!\")\n") while sequence_received = 'n' do inputted_string = get(0) if inputted_string[1] = 0 then if sequence(inputted_string[2]) then sequence_received = 'y' end if end if end while puts(1,"\nThank you! Program Finished!\n")To convert a character string to either an atom or a sequence value, you use the value() library routine:

include get.e rs = value(s)

This works the same way as get(), as it returns a two element sequence value, the first element being the error code, and the second element, if successful, being the actual atom or sequence value equivalent of the character string. The only difference is that the source is a sequence variable (s) and the destination is a receiving sequence variable (rs). A demo is available showing how three character strings are converted to atom and sequence values.

include get.e sequence character_strings, value_string object actual_value character_strings = {"45.99","{1,2,3,{4,4},5}","\"Euphoria\""} for ix = 1 to 3 do printf(1,"Character String: %s\n",{character_strings[ix]}) value_string = value(character_strings[ix]) actual_value = value_string[2] puts(1,"Euphoria Data Object After value(): ") print(1,actual_value) puts(1,"\n\n") end forYou can also convert a Euphoria data object into a character string, or as part of a formatted character string by using sprintf():

rs = sprintf(s,o)

sprintf() is similar to printf(), as it takes either an atom value or a sequence representing a list of values (o), which it then edits by using a format string (s). The format codes introduced in our discussion on

sequence print_line for line = 1 to 5 do print_line = sprintf("This is line %03d\n", line) puts(1,print_line) end forAnother way of changing the state of a data object is by altering the case of alphabetic characters. Euphoria has two library routines that can do this for you.

To change any characters between 'A' and 'Z' in both atom and sequence data objects to lowercase, you use the lower() library routine:

include wildcard.e ro = lower(o)

With lower(), a character string of "Euphoria" and "EUPHORIA" passed to this library routine as o would produce a string of "euphoria" that is then stored in the receiving variable ro. With single atom values like 'J' or 74, what is stored in the receiving variable is 'j' or 106. The opposite of lower() is upper(), which coverts any characters in both atoms and sequences that are between 'a' and 'z' to uppercase:

include wildcard.e ro = upper(o)

A character string of "Euphoria" and "euphoria" passed to upper(),as o would produce a string of "EUPHORIA", which is then stored in the receiving variable ro. With single atom values like 'z' or 122, upper() will store 'Z' or 90 in the receiving variable. A demo program is available to show how upper() and lower() works.

include wildcard.e sequence test_string, uppered, lowered test_string = "This is a fun way to learn Euphoria!" uppered = upper(test_string) lowered = lower(test_string) printf(1,"Original String : %s\n",{test_string}) printf(1," After lower() : %s\n",{lowered}) printf(1," After upper() : %s\n",{uppered})Earlier in the tutorial, we introduced the & operator, which allows you to join atom or sequence data objects together to create new sequences. Euphoria offers other ways of linking data objects together, the first being

rs = append(s,o)

append() creates a new sequence, which is stored in variable rs, by adding o to the end of s as an entire element. So how does this differ from the & operator? Do we really need append() anyways? When & joins any two or more data objects, it creates a sequence that is as long as the total number of elements made up by the participating data objects. However, append() will always create a new sequence that is one element longer than the length of s. The reason for this is because append() will make o the new last element of s. When only atoms are involved in the joining, & and append()'s efforts produce the same result. The difference is only apparent when sequences are involved. A demo program is available to help you clarify the usage of append(), with mention being made to show how it differs from the & operator.

atom atom1, atom2 sequence seq1, seq2, union atom1 = 83 atom2 = 4 seq1 = {1,1,1,1} seq2 = {2,2} puts(1,"& And append()\n") puts(1,"==============\n\n") printf(1,"Atom Values: %d, %d\n",{atom1,atom2}) puts(1," Using &: ") union = atom1 & atom2 print(1,union) puts(1,"\n") puts(1," Using append(): ") union = {} union = append(union,atom1) union = append(union,atom2) print(1,union) puts(1,"\n\n") puts(1,"Sequence Values: ") print(1,seq1) puts(1," ") print(1,seq2) puts(1,"\n") puts(1," Using &: ") union = seq1 & seq2 print(1,union) puts(1,"\n") puts(1," Using append(): ") union = {} union = append(seq1,seq2) print(1,union) puts(1,"\n\n")A related library routine to append() is prepend():

rs = prepend(s,o)

prepend() creates a new sequence, which is stored in variable rs, by adding o to the beginning of s as a single element. This means o becomes the new first element of s, or s[1] in Euphoria terms. Like append(), prepend() creates a new sequence that is one element longer than

atom atom1, atom2 sequence seq1, seq2, union atom1 = 83 atom2 = 4 seq1 = {1,1,1,1} seq2 = {2,2} puts(1,"& And prepend()\n") puts(1,"==============\n\n") printf(1,"Atom Values: %d, %d\n",{atom1,atom2}) puts(1," Using &: ") union = atom1 & atom2 print(1,union) puts(1,"\n") puts(1," Using prepend(): ") union = {} union = prepend(union,atom1) union = prepend(union,atom2) print(1,union) puts(1,"\n\n") puts(1,"Sequence Values: ") print(1,seq1) puts(1," ") print(1,seq2) puts(1,"\n") puts(1," Using &: ") union = seq1 & seq2 print(1,union) puts(1,"\n") puts(1," Using prepend(): ") union = {} union = prepend(seq1,seq2) print(1,union) puts(1,"\n\n")Both append() and prepend() are handy when you want to stack atoms and sequences in a queue, like candies in a "PEZ" dispenser. When append() or prepend() is used to attach a data object to a larger sequence, that data object retains its separate identity. Sequences can also be created "on the fly" by repeating a value so many times. The repeat() library routine was created to perform just that:

rs = repeat(o,a)

repeat() creates a sequence value a elements long, where each element has the value of o. repeat() can create sequences that are made up of atom or

sequence elements. These created sequences can be as long as you need them to be. A demo is available showing some sequences made by repeat().

puts(1,"Some Sequences Created By repeat()\n\n") puts(1,"repeat(20,10):\n ") print(1,repeat(20,10)) puts(1,"\n\n") puts(1,"repeat({1,1,1,1,1},5):\n ") print(1,repeat({1,1,1,1,1},5)) puts(1,"\n\n") puts(1,"repeat(\"Tim\",4):\n ") print(1,repeat("Tim",4)) puts(1,"\n")Once you've created your sequences, it's a good idea to know how large they are if you plan to examine one or many of the elements that make the sequence up. The length() library routine returns the length of a sequence in number of elements:

ri = length(s)

The length of the sequence, shown here as s, is placed in the receiving variable ri. Note that the returned value is how long sequence s is in elements, not atoms. This means the sequences {{1,1},{1,1}} and {1,1} are the same length in elements, even though the former contains more atoms. A sequence value of {}, which is a null sequence, returns a value of 0. A demo program is available to show how length() can be used in a "for" statement to create a loop that adjusts to the size of a sequence.

sequence seq seq = {{0,0,0},{65,{7,7,7},23.1},"Timmy"} for element1 = 1 to 3 do puts(1,"Elements For Sequence ") print(1,seq[element1]) puts(1,":\n") for element2 = 1 to length(seq[element1]) do printf(1," Element %d: ",element2) print(1,seq[element1][element2]) puts(1,"\n") end for puts(1,"\n") end forThe next chapter will introduce library routines that search sequences!

ToC

Advanced Data Object Handling, Part Two

As you build larger, more complex sequence data objects, you are going to need tools that can search them quickly. One way is to write some code using "if", "while", and "for" statements that compare each element against a specific value. A much easier way is to take advantage of Euphoria's sequence search library routines. These library routines allow the programmer to find elements either using a specific value or a partial search string.

ri = find(o,s)

find() searches sequence

the first match. A demo program demonstrates the use of find() and also how to match more than one element using find().

atom found, more_finds, offset sequence search_string search_string = {34,5,106,72,65,5,90,17,5,13} puts(1,"Searching Sequence ") print(1,search_string) puts(1," For 5\n\n") offset = 0 more_finds = 'y' while more_finds = 'y' do found = find(5,search_string) if found then offset = offset + found printf(1,"5 Found As Element %d\n",offset) search_string = search_string[found+1..length(search_string)] else more_finds = 'n' end if end while puts(1,"\nProgram completed\n")While find() finds a single element in a sequence, match() allows you to search a sequence for a specfic group of elements:

ri = match(s1,s2)

match() searches sequence s2 in order to find a sequence of elements, shown here as s1. If successful, receiving variable ri is assigned the element number in s2 where the first element of s1 is located. What would each of the receiving variables contain after each match()?

atom element_id1, element_id2, element_id3 element_id1 = match("Al", "David Alan Gay") element_id2 = match({3,4,5}, {1,2,3,4,5,6,7,8,9,0}) element_id3 = match({{50,23,4},{-1,-2}}, {{15,89},{50,23,4},{-1,-2}})

Here are the results:

"David Alan Gay" - element_id1 is assigned the value of 7 {1,2,3,4,5,6,7,8,9,0} - element_id2 is assigned the value of 3 {{15,89}, {50,23,4},{-1,-2}} - element_id3 is assigned the value of 2

If match() cannot find what it is looking for in the searched sequence, then 0 is returned. A demo program is available to further clarify match() if needed.

sequence nursery_rhyme atom found nursery_rhyme = "Jack and Jill went up the hill to fetch a pail of water" puts(1,nursery_rhyme & "\n\n") printf(1,"Searching String For \"%s\"\n",{"hill"}) found = match("hill",nursery_rhyme) printf(1," Found \"%s\" beginning at element %d\n\n",{"hill",found}) printf(1,"Searching String For \"%s\"\n",{"l of water"}) found = match("l of water",nursery_rhyme) printf(1," Found \"%s\" beginning at element %d\n\n",{"l of water", found}) puts(1, "Searching String For ") print(1, {97,110,100}) puts(1,"\n") found = match({97,110,100},nursery_rhyme) puts(1," Found ") print(1, {97,110,100}) printf(1," beginning at element %d\n\n",found)The previous library routines we introduced in this chapter used an actual value to match one or a specific group of elements in a sequence. The next library routine uses "wildcards" to search for elements in sequences. Before we introduce this library routine, we need to discuss what wildcards are. Wildcards are single substitution characters used in conjunction with other characters to make a generic search string. You may have seen wildcards at work when you use the DIR command in DOS as follows:

dir *.com - lists all files ending with an extension of .COM

Euphoria has two wildcard characters that closely follow DOS' version:

* - matches any 0 or more characters.

For example, a generic search string of "A*" will match values like "Apple", "Acorn", and "A". A generic search string of "*e" would match values like "value"

"cue", and "e".

? - matches any single character.

A generic search string of "g???" for example would match values like "game", "gone", and "goat". A generic search string of "??t" would match values like "Cat",

"dot", and "Hit". With wildcards now explained, we can introduce the wildcard_match() library routine.

include wildcard.e ri = wildcard_match(s1,s2)

wildcard_match() checks if sequence s2 matches the wildcard pattern defined in sequence s1. A 1 is returned if s2 matches s1, otherwise a 0 is returned. The return value is stored in the receiving variable ri. The use of matching sequence values with wildcard patterns is a little tricky. This is because wildcard_match() takes into consideration both alphabetic case and the placement of the wildcard characters in the pattern string. Look at the examples on the next page to see some common mistakes when using wildcard_match().

atom match_1, match_2, match_3 match_1 = wildcard_match("a*","ABCDEFGHIJKLMNOPQRSTUVWXYZ") match_2 = wildcard_match("?Z","ABCDEFGHIJKLMNOPQRSTUVWXYZ") match_3 = wildcard_match("PQR*","ABCDEFGHIJKLMNOPQRSTUVWXYZ")All three variables above will be assigned a value of 0. The first wildcard_match() line won't match because the pattern has a lowercase letter while the searched string is all in uppercase. The second wildcard_match() will not match because ? was used instead of *, even though "Z" is indeed the last letter of the searched string. "?Z" means any two character string, the second character being "Z". The last wildcard_match() line will not match because even though "PQR" does exist in the searched string, the pattern "PQR*" implies a match only if the searched string begins with "PQR". If you want to search for a substring of characters anywhere in the target sequence, place a * wildcard on both sides of the substring. This will produce a match pattern of "*PQR*" for example. You can also mix wildcards in a match pattern, such as "A?c*1". This means wildcard_match() will only get a match on this pattern if the searched string's first character is "A", its third character is "c", and its last character is "1". A demo program is available that allows you to experiment with pattern strings, to help you get a better understanding of wildcard_match().

include wildcard.e sequence pattern_string, search_string atom halt_program, matched halt_program = 'n' search_string = "wildcard_match() is a powerful feature of Euphoria." while halt_program = 'n' do puts(1,search_string & "\n") puts(1,"Enter a wildcard pattern string or \"STOP\": ") pattern_string = gets(0) pattern_string = pattern_string[1..(length(pattern_string)-1)] if compare(pattern_string,"STOP") = 0 then halt_program = 'y' else matched = wildcard_match(pattern_string, search_string) puts(1,"\n") if matched then puts(1,pattern_string & " matches above string.\n\n") else puts(1,pattern_string & " does not match above string.\n\n") end if end if end whileIf you want to write your own sequence search programs, one important factor is the speed on finding the element you are searching for. To optimize your search, it is best to sort the sequence and then look up each element until you either get a match, or compare a value that is larger than the element you are looking for (which means not found). To sort a sequence to be searched, you use the

include sort.e rs = sort(s)

sort() sorts sequence s, but does not change s. The sorted sequence is instead stored in receiving variable rs. The sequence to be sorted can be made up of any combination of atoms and sequences. The result of the sort will be a sequence with its elements sorted in ascending order, with any atom values appearing first before sequence elements. Elements that are sequences are sorted based on an element by element comparison, starting with the first element onward. The comparison is based on the value of each element. Run a demo program now that demonstrates how sort() works with sample data.

include sort.e sequence sorted, unsorted unsorted = {"world","the","Euphoria","rules"} sorted = sort(unsorted) puts(1,"Unsorted: ") for words = 1 to length(unsorted) do puts(1,unsorted[words] & " ") end for puts(1,"\n") puts(1,"Sorted: ") for words = 1 to length(sorted) do puts(1,sorted[words] & " ") end for puts(1,"\n\n") unsorted = {{1,1,8,2},5,{1,2,3},{1,1,9},-45,{1,2,1}} sorted = sort(unsorted) puts(1,"Unsorted: ") print(1,unsorted) puts(1,"\n") puts(1,"Sorted: ") print(1,sorted) puts(1,"\n")This concludes your introduction to advanced data object handling library routines. The next chapter will introduce library routines that can enhance any arithmetic computations you may have in your programs.

ToC

Advanced Arithmetic Library Routines

Euphoria offers a set of library routines that save the programmer from coding complex programs to handle advanced math formulas. These library routines can handle some algebra and trigonometry formulas, rounding down of numbers to the smallest whole number, and even the generation of random numbers. Note that some of these library routines require a strong background in certain areas of mathematics before you attempt to use them. The first library routine we will introduce is

ro = sqrt(o)

sqrt() returns the square root of o, which is then stored in variable ro. To refresh your memory, a square root of a number is any number that is multiplied by itself to produce a number. For example, the square root of 25 is 5, because 5 times 5 gives 25. This library routine can accept both atom and sequence data objects. For sequence data objects passed to sqrt(), a sequence with the same length is generated, with each atom element a sequare root of the elements in the original sequence. For example, {16,0.25} passed to sqrt() returns a value of {4,0.5}. The one word of caution is not to pass a negative atom value or a sequence with any negative atom elements to sqrt(), or your program will encounter a runtime error message. A demo program is available showing how atoms and sequences are squared using sqrt().

atom value1 sequence value2 value1 = 25 value2 = {81,{9,4},100} puts(1, "The square root of ") print(1,value1) puts(1, " is ") print(1,sqrt(value1)) puts(1,"\n") puts(1, "The square root of ") print(1,value2) puts(1, " is ") print(1,sqrt(value2)) puts(1,"\n")The opposite of square-rooting a number is to raise a number by the power of 2. Euphoria has a library routine that can raise a number to the power 2, or any power for that matter. Here is the library routine used to raise a data object value by a given power:

ro = power(o1,o2)

power() raises o1 to the power of o2, the result being stored in receiving variable ro. Because power() can use raise both atoms and sequence data objects to

a power that in itself can also be either an atom or a sequence data object, conversion of values must first occur. If o1 is an atom and o2 is a sequence, Euphoria converts o1 to a sequence value that is the same length as o2, composed of elements that equal the value of o1. If o1 is a sequence and o2 is an atom, Euphoria converts o2 to a sequence that is the same length as o1, composed of elements equalling the value of o2. This concept was introduced in "Assigning Values To Sequence Variables". Only after any needed conversion of atoms to sequence data objects is completed does

atom base, result sequence base2, result2 base = 2 for exponent = 1 to 4 do result = power(base,exponent) printf(1, "%d to the power of %d is %d\n",{base,exponent,result}) end for puts(1,"\n") for exponent = 1 to 4 do base2 = repeat(10,5) result2 = power(base2,exponent) print(1, base2) puts(1, " to the power of ") print(1,exponent) puts(1, " is ") print(1,result2) puts(1,"\n") end for puts(1, "\n") base = 3 result2 = power(base,{2,3,4}) print(1, base) puts(1, " to the power of ") print(1,{2,3,4}) puts(1, " is ") print(1,result2) puts(1,"\n")Another library routine related to sqrt() is log():

ro = log(o)

log() returns the natural logarithm of a number, o. A logarithm is the exponent that raises a number, called the base, to a specific power. A natural logarithm is the exponent that raises a base number of 2.71828 (approximately) to a specific power. For example, log(27) will return a value of 3.29584. If you were to raise 2.71828 to the power of 3.29584 by using power(), you would get back a value of 26.9999. log() can handle both atoms and sequences in the same fashion sqrt() can, returning a value that is stored in a receiving variable, named ro here. One note: do not pass a negative number or a zero to log(), or your program will encounter a runtime error. Natural logarithms are used in calculations like statistics and trigonometry, so it is unlikely you will use this library routine much, unless you are working in areas of mathematics that require natural logarithms. A demo program is available if you want to see some examples of how log() is used.

atom log1, base, result1 sequence log2, result2 base = 2.71828 log1 = log(63) log2 = log({100,50,25}) result1= power(base,log1) result2= power(base,log2) printf(1,"The natural logarithm of 63 is %f\n",log1) printf(1,"%.5f to the power of %f is %.0f (rounded up)\n\n", {base,log1,result1}) puts(1,"The natural logarithm of ") print(1,{100,50,25}) puts(1," is ") print(1,log2) puts(1,"\n") printf(1,"%.5f to the power of ",base) print(1,log2) puts(1," is ") printf(1,"{%.0f,%.0f,%.0f} (rounded up)\n\n",result2)When one number does not evenly divide by another, have you wondered what the decimal part of the result means? Well, the numbers to the right of the decimal is what is left over, shown as a decimal fraction. For example, 3.5 returned from dividing 7 by 2 means "3 with 1 left over". How did we get 1? Multiply .5 by the divisor 2 and you get 1. Sometimes this remainder of a division is just as important as the result, and usually it is preferable to have that remainder shown as a whole number. You could use the technique shown above to determine the remainder, or you can use this next Euphoria library routine instead.

ro = remainder(o1,o2)

remainder() returns the remainder left over from dividing o1 by o2, and stores it in the receiving variable ro. The value returned by remainder() is always less than the value of o2, the divisor. Because remainder() can be either atoms or sequences for both the quotient and the divisor, Euphoria will convert an atom to a sequence that will match the length of the other sequence before remainder() is executed. The elements of this new other sequence will have the value of the original atom value. You may want to review power() or the chapter "Assigning Values To Sequence Variables" if you need any help in understanding this. A demo program is available that uses various examples of remainder().

sequence format_string atom leftovers, result format_string = "10 divided by %d goes %d time(s) with %d left over.\n" for divisor = 2 to 9 do leftovers = remainder(10,divisor) result = 10/divisor printf(1,format_string,{divisor,result,leftovers}) end forIf you are working with floating point numbers and want to convert that value to an integer, one way to do it is to use the

ro = floor(o)

object value1, value2 value1 = 5.3 value2 = -5.3 printf(1,"%.1f floor()'d gives %d\n",{value1,floor(value1)}) printf(1,"%.1f floor()'d gives %d\n",{value2,floor(value2)}) puts(1,"\n") value1 = {35.3,-46.1,22.9,-.7345} print(1, value1) puts(1," floor()'d gives ") print(1, floor(value1)) puts(1,"\n")Euphoria has a library routine that may be of interest to those who want to write their own games. It involves the creation of random numbers, a key element in any game, whether it involves randomly generating a number to guess, or creating an unexpected malfunction aboard a spaceship during a critical moment in battle. Here is the syntax of the

ro = rand(o)

include get.e atom random_number,guessed_number, end_program sequence input_data puts(1,"Guess any number between 1 and 10\n") end_program = 'n' random_number = rand(10) while end_program = 'n' do input_data = get(0) if input_data[1] = 0 then if integer(input_data[2]) then guessed_number = input_data[2] if guessed_number = random_number then end_program = 'y' puts(1,"\nBingo!\n") elsif guessed_number < random_number then puts(1,"\nYou're too low! Try again!\n") elsif guessed_number > random_number then puts(1,"\nYou're too high! Try again!\n") end if end if end if end whileLeft on its own,

include machine.e set_rand(a)

If you execute

include get.e include machine.e atom seed, end_program sequence input_data, prompt, line1, line2, line3 line1 = "\nYou entered " line2 = " as the value to set the random number generator seed to.\n" line3 = "The next 10 numbers generated by rand(100) will always be:\n" prompt = "\nEnter any value or 0 to end this program program\n" end_program = 'n' while end_program = 'n' do puts(1,prompt) input_data = get(0) if input_data[1] = 0 then if integer(input_data[2]) then seed = input_data[2] if seed != 0 then puts(1,line1) print(1,seed) puts(1,line2) set_rand(seed) puts(1,line3) for i = 1 to 10 do print(1,rand(100)) puts(1, " ") end for puts(1,"\n") else end_program = 'y' end if end if end if end whileTo close this chapter, we will briefly introduce a series of library routines devoted to trigonometry:

- Returns the sine of an anglero = sin(o)- Returns the cosine of an anglero = cos(o)- Returns the tangent of an anglero = tan(o)- Returns the angle of a tangent (opposite of tan())ro = arctan(o)

The first three library routines accept an atom or a sequence value,

ToC