Документация
HTML CSS PHP PERL другое

Section A.4.  Answers to Chapter 5 Exercises

 
Previous
Table of Contents
Next

A.4. Answers to Chapter 5 Exercises

  1. Here's one way to do it:

        print reverse <>;
    

    Well, that's pretty simple! 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" repeatedly, or copy and pasted 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,[*] "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).[Section A.4.  Answers to Chapter 5 Exercises] Mysteriously, the output is off by one space. So, what went wrong?

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

    [Section A.4.  Answers to Chapter 5 Exercises] Unless Larry told you not to do that.

    The problem happens when Perl counts 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 14 spaces and a six-character string, sure that it gives the 20 characters you asked for in "%20s". Oops.

    Perl isn't looking at the contents of the string to determine the width; it is checking the raw number of characters. A newline (or another special character, such as a tab or a null character) will throw things off.[Section A.4.  Answers to Chapter 5 Exercises]

    [Section A.4.  Answers to Chapter 5 Exercises] As Larry should have explained to you by now.

    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. 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 exercise answers.

    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, which is the wrong variable. If you forgot how to use curly braces to do this, 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 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, $_;
    

    Previous
    Table of Contents
    Next
    © 2000- NIV