Week1 |

By now, you know enough about programming in Perl to write programs that perform many useful tasks. The program in Listing R1.1, which takes a number and prints out its English equivalent, illustrates some of the concepts you've learned during your first week.

Listing R1.1. Printing the English equivalent of numeric input.

1: #!/usr/local/bin/perl 2: 3: # define the strings used in printing 4: @digitword = ("", "one", "two", "three", "four", "five", 5: "six", "seven", "eight", "nine"); 6: @digit10word = ("", "ten", "twenty", "thirty", "forty", 7: "fifty", "sixty", "seventy", "eighty", "ninety"); 8: @teenword = ("ten", "eleven", "twelve", "thirteen", "fourteen", 9: "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"); 10: @groupword = ("", "thousand", "million", "billion", "trillion", 11: "quadrillion", "quintillion", "sextillion", "septillion", 12: "octillion", "novillion", "decillion"); 13: 14: # read a line of input and remove all blanks, commas and tabs; 15: # complain about anything else 16: $inputline = <STDIN>; 17: chop ($inputline); 18: $inputline =~ s/[, \t]+//g; 19: if ($inputline =~ /[^\d]/) { 20: die ("Input must be a number.\n"); 21: } 22: 23: # remove leading zeroes 24: $inputline =~ s/^0+//; 25: $inputline =~ s/^$/0/; # put one back if they're all zero 26: 27: # split into digits: $grouping contains the number of groups 28: # of digits, and $oddlot contains the number of digits in the 29: # first group, which may be only 1 or 2 (e.g., the 1 in 1,000) 30: @digits = split(//, $inputline); 31: if (@digits > 36) { 32: die ("Number too large for program to handle.\n"); 33: } 34: $oddlot = @digits % 3; 35: $grouping = (@digits-1) / 3; 36: 37: # this loop iterates once for each grouping 38: $count = 0; 39: while ($grouping >= 0) { 40: if ($oddlot == 2) { 41: $digit1 = 0; 42: $digit2 = $digits[0]; 43: $digit3 = $digits[1]; 44: $count += 2; 45: } elsif ($oddlot == 1) { 46: $digit1 = 0; 47: $digit2 = 0; 48: $digits = $digits[0]; 49: $count += 1; 50: } else { # regular group of three digits 51: $digit1 = $digits[$count]; 52: $digit2 = $digits[$count+1]; 53: $digit3 = $digits[$count+2]; 54: $count += 3; 55: } 56: $oddlot = 0; 57: if ($digit1 != 0) { 58: print ("$digitword[$digit1] hundred "); 59: } 60: if (($digit1 != 0 || ($grouping == 0 && $count > 3)) && 61: ($digit2 != 0 || $digit3 != 0)) { 62: print ("and "); 63: } 64: if ($digit2 == 1) { 65: print ("$teenword[$digit3] "); 66: } elsif ($digit2 != 0 && $digit3 != 0) { 67: print ("$digit10word[$digit2]-$digitword[$digit3] "); 68: } elsif ($digit2 != 0 || $digit3 != 0) { 69: print ("$digit10word[$digit2]$digitword[$digit3] "); 70: } 71: if ($digit1 != 0 || $digit2 != 0 || $digit3 != 0) { 72: print ("$groupword[$grouping]\n"); 73: } elsif ($count <= 3 && $grouping == 0) { 74: print ("zero\n"); 75: } 76: $grouping-; 77: }

$ programR1_1 11,683 eleven thousand six hundred and eighty-three $

This program reads in a number up to 36 digits long and prints out its English equivalent, using one line for each group of three digits.

Lines 4-12 define array variables whose lists are the possible
words that can be in a number. The variable `@digitword`
lists the digits; `@digit10word` lists the words that indicate
multiples of ten; `@teenword` lists the words that represent
the values from 11 to 19; and `@groupword` lists the names
for each group of digits. Note that some of these lists have an
empty first element; this ensures that the array subscripts refer
to the correct value. (For example, without the empty word at
the beginning of `@digitword`, `$digitword[5]` would
refer to `four`, not `five`.)

Lines 14-21 read the input and check whether it is valid. Valid numbers consist of digits optionally separated by spaces, tabs, or commas. The substitution operator in line 18 removes these valid separators; the conditional expression in line 19 checks whether any invalid separators exist.

If the program reaches line 24, the input number is valid. Line
24 gets rid of any leading zeros (to ensure that, for example,
`000071` is converted to `71`). If a number consists
entirely of zeros, line 24 converts `$inputline` to the
empty string; line 25 tests for this empty string and adds a zero
if necessary.

Lines 30-35 split the number into individual digits and create
a list consisting of these digits. This list is assigned to the
array variable `@digits`. Line 34 determines whether the
first group of digits contains fewer than three digits; an example
of this is the number `45,771`, whose first group of digits
consists of only two digits. The scalar variable `$oddlot`
is assigned the number of digits in the first group if the group
is an odd lot of one or two; it is assigned `0` if the
first group of digits contains all three digits.

Line 35 calculates the number of groups of digits (including the initial odd lot). This determines the number of times that the upcoming printing loop is to be iterated.

Lines 38-79 actually print the English value for this number.
Each group of three digits is printed on its own line. The scalar
variable `$count` contains the number of digits printed
so far and is used as a subscript for the array variable `@digits`.

To actually print the English value corresponding to a group of
three digits, this loop first executes lines 40-57, which assign
the values of the digits in the group to three scalar variables:
`$digit1`, `$digit2`, and `$digit3`. If the
group being handled is the first group, lines 40 and 46 check
whether the group is an odd lot. For example, if the first group
contains only two digits, the condition in line 40 becomes true,
and the variable `$digit1`, which represents the first
digit of the group, is assigned `0`. Using `$digit1`,
`$digit2`, and `$digit3` reduces the complexity
of the program because no code following line 57 has to check
for the value of `$oddlot`.

The number of digits actually handled is added to the scalar variable
`$count` at this point.

Line 58 assigns `0` to `$oddlot`. Subsequent groups
of digits always contain three digits.

Lines 59-77 print the English value associated with this particular group of digits as follows:

- Lines 59-61 print the value of the hundreds place in this group (the first of the three digits).
- Lines 62-64 check whether the word
`and`needs to appear here. The word`and`is required in the following cases:`$digit1`is nonzero and one of the other digits is nonzero (as in`three hundred and four`)`$digit1`is zero, one of the other digits is nonzero, and this is the last group to be handled (as in the`and four`part of the number`11,004`)

- If the second digit is a
`1`(as in`317`), one of the "teen words" (such as`eleven`,`twelve`, and`thirteen`) must be used. Line 66 checks for this condition, and line 67 prints the appropriate word. - If both of the last two digits are defined, they both must
be printed, and a dash must separate them (as in
`forty-two`). Line 69 prints this pair of words and the dash. - If only one of the last two digits is defined, it is printed using line 71. (Note that line 71 actually specifies that both digits are printed; however, because only one is actually nonzero, it is the only one that appears. The digit that is zero appears in the output as the empty string because zero is equivalent to the empty string in Perl.)
- Lines 73-74 print the word associated with this group of digits.
For example, if this group is the second-last group of digits,
the word
`thousand`is printed. - Line 75 handles the special case of the number
`0`. In this case, the word`zero`is printed.

Once the English value for a particular group of digits is printed,
the scalar variable `$grouping` has its value decreased
by one, and the program continues with the next group of digits.
If there are no more digits to print, the program terminates.