home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Book HomeLearning Perl, 3rd EditionSearch this book

A.5. Answers to Chapter 6 Exercises

  1. Here's one way to do it:

    print reverse <>;

    Well, that's pretty simple! But it works because print is looking for a list of strings to print, which it gets by calling reverse in a list context. And reverse is looking for a list of strings to reverse, which it gets by using the diamond operator in list context. So, the diamond returns a list of all of the lines from all of the files of the user's choice. That list of lines is just what cat would print out. Now reverse reverses the list of lines, and print prints them out.

  2. Here's one way to do it:

    print "Enter some lines, then press Ctrl-D:\n";  # or Ctrl-Z
    chomp(my @lines = <STDIN>);
    
    print "1234567890" x 7, "12345\n";  # ruler line to column 75
    
    foreach (@lines) {
      printf "%20s\n", $_;
    }

    Here, we start by reading in and chomping all of the lines of text. Then we print the ruler line. Since that's a debugging aid, we'd generally comment-out that line when the program is done. We could have typed "1234567890" again and again, or even used copy-and-paste to make a ruler line as long as we needed, but we chose to do it this way because it's kind of cool.

    Now, the foreach loop iterates over the list of lines, printing each one with the %20s conversion. If you chose to do so, you could have created a format to print the list all at once, without the loop:

    my $format = "%20s\n" x @lines;
    printf $format, @lines;

    It's a common mistake to get 19-character columns. That happens when you say to yourself,[385] "Hey, why do we chomp the input if we're only going to add the newlines back on later?" So you leave out the chomp and use a format of "%20s" (without a newline).[386] And now, mysteriously, the output is off by one space. So, what went wrong?

    [385]Or to Larry, if he's standing nearby.

    [386]Unless Larry told you not to do that.

    The problem happens when Perl tries to count the spaces needed to make the right number of columns. If the user enters hello and a newline, Perl sees six characters, not five, since newline is a character. So it prints fourteen spaces and a six-character string, sure that it gives the twenty characters you asked for in "%20s". Oops.

    Of course, Perl isn't looking at the contents of the string to determine the width; it merely checks the raw number of characters. A newline (or another special character, such as a tab or a null character) will throw things off.[387]

    [387]As Larry should have explained to you by now.

  3. Here's one way to do it:

    print "What column width would you like? ";
    chomp(my $width = <STDIN>);
    
    print "Enter some lines, then press Ctrl-D:\n";  # or Ctrl-Z
    chomp(my @lines = <STDIN>);
    
    print "1234567890" x (($width+9)/10), "\n";      # ruler line as needed
    
    foreach (@lines) {
      printf "%${width}s\n", $_;
    }

    This is much like the previous one, but we ask for a column width first. We ask for that first because we can't ask for more input after the end-of-file indicator, at least on some systems. Of course, in the real world, you'll generally have a better end-of-input indicator when getting input from the user, as we'll see in later chapters.

    Another change from the previous exercise's answer is the ruler line. We used some math to cook up a ruler line that's at least as long as we need, as suggested as an "extra credit" part of the exercise. Proving that our math is correct is an additional challenge. (Hint: Consider possible widths of 50 and 51, and remember that the right side operand to x is truncated, not rounded.)

    To generate the format this time, we used the expression "%${width}s\n", which interpolates $width. The curly braces are required to "insulate" the name from the following s; without the curly braces, we'd be interpolating $widths, the wrong variable. If you forgot how to use curly braces to do this, though, you could have written an expression like '%' . $width . "s\n" to get the same format string.

    The value of $width brings up another case where chomp is vital. If the width isn't chomped, the resulting format string would resemble "%30\ns\n". That's not useful.

    People who have seen printf before may have thought of another solution. Because printf comes to us from C, which doesn't have string interpolation, we can use the same trick that C programmers use. If an asterisk ("*") appears in place of a numeric field width in a conversion, a value from the list of parameters will be used:

    printf "%*s\n", $width, $_;


Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.