Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
812,44 KB
Nội dung
356 CHAPTER 10 LOOPING FACILITIES From a Perlish perspective, you can think of select as a special kind of interactive variation on a foreach loop. But rather than having each list-value assigned auto- matically to the loop variable for one iteration, select only assigns values as they are selected by the user. Next, you’ll see how you can avoid “re-inventing wheels” by using this loop. 10.7.1 Avoiding the re-invention of the “choose-from-a-menu” wheel Although Perl has no counterpart to the Shell’s handy select loop, its functionality is provided by a CPAN module called Shell::POSIX::Select. 21 It provides its services through source-code filtering, which means it extracts the select loops from your program and rewrites them using native Perl features. As a result, you can use a feature that’s missing from Perl as if it were there! The benefit of bringing the select loop to Perl is that it obviates the need for ter- minal applications to provide their own implementations of the choose-from-a-menu code, which indulges the programmer’s noble craving for Laziness—and thereby increases productivity. Table 10.9 shows the syntax variations for the Shell’s version of the select loop. If in LIST is omitted (as in Form 0), in "$@" is used by default to provide automatic processing of the script’s (or function’s) argument list. Some of the major forms of Perl’s select loop are shown in table 10.10. These take their inspiration from the Shell and then add enhancements for greater friendli- ness and, well, Perlishness. As you can see, Perl’s select lets you omit any or even all of its components (apart from the punctuation symbols). For example, if the loop variable is omitted, as in Forms 0, 1, and 2, $_ is used by default. If the LIST is omitted, as in Forms 0 and 1, the appropriate arguments are used by default (i.e., those provided to the script or the 21 Written by yours truly, a long-time Shell programmer turned Perl proponent, while writing this chap- ter—so I wouldn't have to say “the best Shell loop is missing from Perl”. Table 10.9 The Shell’s select loop select var ; do commands; done # Form 0 select var in LIST; do commands; done # Form 1 Ta b l e 10 . 1 0 T h e select loop for Perl use Shell::POSIX::Select; select () { } # Form 0 select () { CODE; } # Form 1 select (LIST) { CODE; } # Form 2 select $var (LIST) { CODE; } # Form 3 THE CPAN’S select LOOP FOR PERL 357 enclosing subroutine), as with its Shell counterpart. And if CODE is omitted (as in Form 0), a statement that prints the loop variable is used as the default code block. Because system administrators have the responsibility for monitoring user activity on their systems, they might find the following application of select to be of par- ticular interest. 10.7.2 Monitoring user activity: the show_user script This program allows the user to obtain a system-activity report for users who are cur- rently logged in: $ cat show_user #! /usr/bin/perl –wl use Shell::POSIX::Select; # Get list of who's logged in @users=`who | perl -wnla -e ' print \$F[0]; ' | sort -u`; chomp @users; # remove newlines # Let program's user select Unix user to monitor select ( @users ) { system "w $_"; } This script uses the who command to get the list of current users, and then a separate Perl command to isolate their names from the first column of that report. Note the need to backslash the $ to prevent the Perl script from providing its own (null) value for $F[0] before the who | perl | sort pipeline is launched. sort is used with the “unique lines” option to remove duplicate user names for those logged in more than once. The w command, which reports the selected user’s activity, won’t appreciate finding newlines attached to the ends of those names, so the @users array is chomp’d to remove them. Here’s a sample run of the script: $ show_user 1) phroot 2) tim Enter number of choice: 2 3:51pm up 4 days, 17:57, 7 users, load average: 0.00, 0.00, 0.00 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT tim pts/1 lumpy Mon10am 3days 18.91s 1.19s -bash tim pts/3 stumpy Mon10am 28:16m 0.48s 0.48s bash -login tim tty5 grumpy Sun 3pm 28:16m 1.71s 1.04s slogin lumpy tim pts/0 bumpy Sun 4pm 1.00s 4.03s 0.14s w tim <ENTER> 1) phroot 2) tim Enter number of choice: <^D> 358 CHAPTER 10 LOOPING FACILITIES Note that the user pressed <ENTER> to redisplay the menu and <^D> to exit the loop, just as she’d do with the Shell’s select. 22 Next, you’ll see how select can facilitate access to Perl’s huge collection of online man pages. 10.7.3 Browsing man pages: the perlman script One of the obstacles faced by all Perl programmers is determining which one of Perl’s more than 130 cryptically named man pages covers a particular subject. To make this task easier, I wrote a script that provides a menu interface to Perl’s online documentation. Figure 10.1 shows the use of perlman, which lets the user choose a man page from its description. For simplicity’s sake, only a few of Perl’s man pages are listed in the figure, and only the initial lines of the selected page are displayed. 23 22 For those who don’t like that behavior (including me), there’s an option that causes the menu to be automatically redisplayed before each prompt. I wish the Shell’s select also had that feature! 23 The select loop is a good example of the benefits of Perl’s source-code filtering facility, which is de- scribed in the selected man page, perlfilter. Figure 10.1 Demonstration of the perlman script THE CPAN’S select LOOP FOR PERL 359 Before we delve into the script’s coding, let’s discuss what it does on a conceptual level. The first thing to understand is that man perl doesn’t produce “the” definitive man page on all things Perlish. On the contrary, its main purpose is to act as a table of contents for Perl’s other man pages, which deal with specific topics. Toward this end, man perl provides a listing in which each man page’s name is paired with a short description of its subject, in this format: perlsyn Perl syntax As illustrated in the figure, the role of perlman is to let the user select a man-page name for viewing from its short description. Listing 10.7 shows the script. Because it’s important to understand which of its elements refer to the man-page names versus their corresponding descriptions, dis- tinctive highlighting with bold type (for man-page names) and underlined type (for descriptions) is used. 1 #! /usr/bin/perl -w 2 3 use Shell::POSIX::Select; 4 5 $perlpage=`man perl`; # put name/description records into var 6 7 # Man-page name & description have this format in $perlpage: 8 # perlsyn Perl syntax 9 10 # Loop creates hash that maps man-page descriptions to names 11 while ( $perlpage =~ /^\s+(perl\w+)\s+(.+ )$/mg ) { # get match 12 13 # Load ()-parts of regex, from $1 and $2 , into hash 14 $desc2page{$2 }=$1; # e.g., $hash{'Perl syntax'}='perlsyn' 15 } 16 17 select $page ( sort keys %desc2page ) { # display descriptions 18 system "man $desc2page{$page}"; # display requested page 19 } The script begins by storing the output of man perl in $perlpage on Line 5. Then a matching operator, as the controlling condition of a while loop (Line 11), is used to find the first man-page name (using “ perl\w+”) and its associated description (using “ .+”) in $perlpage. The m modifier on the matching operator allows the pattern’s leading ^ to match the beginning, and its $ the end, of any of the lines within the variable (see table 3.6 on multi-line mode). Capturing parentheses (see table 3.8) are used in the regex (Line 11) to store what the patterns matched in the special variables $1 and $2 (referring to the first Listing 10.7 The perlman script 360 CHAPTER 10 LOOPING FACILITIES and second set of parentheses, respectively), so that in Line 14 the man-page name can be stored in the %desc2page hash, using its associated description as the key. The next iteration of the loop will look for another match after the end of the pre- vious one, due to the use of the matching operator’s g modifier in the scalar context of while’s condition. 24 Finally, in Lines 17–19, select displays the numbered list of sorted man-page descriptions in the form “7) Perl source filters”. Then it obtains the user’s selection, retrieves its corresponding page name from the hash, and invokes man to display the requested page (in the case of figure 10.1, “perlfilter”). As you might imagine, this script is very popular with the students in our classes, because it lets them find the documentation they need without first memorizing lots of inscrutable man-page names (such as “perlcheat”, “perltoot”, and “perlguts”). TIP You can use the only Shell loop that Larry left out of Perl by getting the Shell::POSIX::Select module from the CPAN. 10.8 SUMMARY Perl provides a rich collection of looping facilities, adapted from the Bourne shell, the C shell, and the C language. The closely-related while and until loops continue iterating until the control- ling condition becomes False or True, respectively. You saw while used to incremen- tally compress images until a target size was reached (in compress_image, section 10.2.2) and to extract and print key/value pairs from a hash with the assistance of the each function (in show_pvars, section 10.2.3). Perl also provides bottom-tested loops called do while and do until, which perform one iteration before first testing the condition. Although these aren’t “real” loops, the savvy programmer can construct functional replacements using while and until with continue blocks to allow loop-control directives to function properly (as shown in confirmation, section 10.6.4). The foreach loop provides the easiest method for processing a list of values, because it frees you from the burden of managing indices. You saw it used to remove files ( rm_files, section 10.4.1) and to perform text substitutions for deciphering acronyms in email messages ( expand_acronyms, section 10.4.4). The relatively complex for loop should be used in cases where iteration can be con- trolled by a condition, and which benefit from its index-management services. An exam- ple is the raffle script (section 10.5.1), which needs to process its arguments in pairs. 24 The meaning of the matching operator’s g modifier is context dependent—in list context, it causes all the matches (or else the captured sub-matches, if any) to be returned at once. But in scalar context, the matches are returned one at a time. SUMMARY 361 The implicit loop provided by the n (or p) option is a great convenience in many small- to medium-sized programs, but larger or more complex ones may have special needs that make the use of explicit loops more practical. 25 You can use the only Shell loop that Larry left out of Perl by getting the Shell::POSIX::Select module from the CPAN. 26 It provides the select loop, which prevents you from having to re-create the choose-from-a-menu code for managing interactions with a terminal user. That loop was featured in pro- grams for browsing Perl’s man pages ( perlman, section 10.7.3) and monitoring users ( show_user, section 10.7.2), which were simplified considerably through use of its services. Directions for further study This chapter provided an introduction to the select loop for Perl, which is a greatly enhanced adaptation of the Shell’s select loop. For coverage of additional features that weren’t described in this chapter, and for additional programming examples, see • http://TeachMePerl.com/Select.html The Shell allows I/O redirection requests to be attached to control structures, as shown in these examples: command | while done for done > file Although Perl doesn’t support an equivalent syntax, you can arrange similar effects using open and Perl’s built-in select function, as explained in these online doc- uments: 27 • perldoc -f open • perldoc -f select • man perlopentut # tutorial on "open" 25 E.g., see the discussion on variable scoping in section 11.3. 26 The downloading procedure is discussed in section 12.2.3. 27 This function selects the default filehandle (see man perlopentut) for use in subsequent I/O op- erations. The select keyword is also used by Shell::POSIX::Select for the select loop, but the intended meaning can be discerned from the context. 362 CHAPTER 11 Subroutines and variable scoping 11.1 Compartmentalizing code with subroutines 363 11.2 Common problems with variables 370 11.3 Controlling variable scoping 373 11.4 Variable Scoping Guidelines for complex programs 376 11.5 Reusing a subroutine 386 11.6 Summary 387 Thinking logically may come naturally to Vulcans like Star Trek’s Mr. Spock, but it’s a challenge for most earthlings. That’s what those millions of VCRs and micro- wave-ovens blinking 12:00 … 12:00 … 12:00—since the 1980s —have been try- ing to tell us. What’s more, even those who excel in logical thinking can experience drastic degra- dations in performance when subjected to time pressures, sleep deprivation, frequent interruptions, tantalizing daydreams, or problems at home—i.e., under normal human working conditions. So, being only human, even the best programmers can find it challenging to design programs sensibly and to write code correctly. Fortunately, computer languages have features that make it easier for earthlings to program well. And any JAPH worth his camel jerky—like you—should milk these features for all they’re worth. One especially valuable programming tool is the subroutine, which is a special struc- ture that stores and provides access to program code. The primary benefits of subrou- tines to (non-Vulcan) programmers are these: COMPARTMENTALIZING CODE WITH SUBROUTINES 363 • They support a Tinkertoy programming mentality, 1 – which encourages the decomposition of a complex programming task into smaller and more easily-understandable pieces. • They minimize the need to duplicate program code, – because subroutines provide centralized access to frequently used chunks of code. • They make it easier to reuse code in other programs, – through simple cutting and pasting. In this chapter, you’ll first learn how to use subroutines to compartmentalize 2 your code, which paves the way for enjoying their many benefits. Then, you’ll learn about the additional coding restrictions imposed by the com- piler in strict mode and the ways they can—and can’t—help you write better programs. We’ll also discuss Perl’s features for variable scoping, which prevent variables from “leaking” into regions where they don’t belong, bumping into other variables, and messing with their values. As we’ll demonstrate in sample programs, proper use of variable-scoping techniques is essential to ensuring the proper functioning of complex programs, such as those having subroutines. During our explorations of these issues, we’ll convert a script from a prior chapter to use a subroutine, and we’ll study cases of accidental variable masking and variable clobberation, so you’ll know how to avoid those undesirable effects. We’ll conclude the chapter by discussing our Variable Scoping Guidelines. These tips—which we’ve developed over many years in our training classes—make it easy to specify proper scopes for variables to preserve the integrity of the data they store. 11.1 COMPARTMENTALIZING CODE WITH SUBROUTINES A subroutine is a chunk of code packaged in a way that allows a program to do two things with it. The program can call the subroutine, to execute its code, and the pro- gram can optionally obtain a return value from it, to get information about its results. Such information may range from a simple True/False code indicating success or fail- ure, through a scalar value, to a list of values. Subroutines are a valuable resource because they let you access the same code from different regions of a program without duplicating it, and also reuse that code in other programs. 1 Tinkertoys were wooden ancestors to Lego toys and their modern relatives. The mentality they all tap into might be called “reductionistic thinking”. 2 The word modularize could be used instead, but in Perl that also means to repackage code as a module, which is something different (see chapter 12). 364 CHAPTER 11 SUBROUTINES AND VARIABLE SCOPING Table 11.1 summarizes the syntax for defining a subroutine, calling it, accessing its arguments (if any), and returning values from it. 3 Table 11.1 Syntax for defining and using subroutines Operation Syntax a Comments Defining a sub sub name { code; } The sub declaration associates the following name with code. Calling a sub name(); # call without args name(ARGS); # call with args $Y=name(); # scalar context call @X=name(ARGS); # list context call A sub’s code is executed by using its name followed by parentheses. Arguments to be passed to the sub are placed within the parentheses. The VALUE(s) (see below) returned by name are automatically converted to scalar form as needed (e.g., for assigning a list to $Y, but not for assigning a list to @X; see text). Returning values return VALUE(s); # returns VALUE(s) print get_time(); # prints time sub get_time { scalar localtime; } # returns formatted time-string return sends VALUE(s) back to the point of call, after converting a list to a scalar if necessary (see above cell). If return has no argument, an empty list or an undefined value is returned (for list/scalar context, respectively). Without return (see get_time), the value of the last expression evaluated is returned. Sensing context if (wantarray) { return @numbers; # list value } else { return $average; # scalar value } wantarray yields True or False according to the list or scalar context of the call, allowing you to return different values for calls in different contexts. Accessing sub arguments ($A, $B)=@_; print $B; # 2nd arg print $_[1]; # 2nd arg $A=shift; $B=shift; print $B; # 2nd arg sub arguments are obtained from the @_ array by copying or shifting its values into named variables, or else by indexing, with $_[0] referencing the first element of @_, $_[1] the second, etc. b a. ARGS stands for one or more values. VALUE(s) is typically a number or a variable. b. The elements of @_ act like aliases to the arguments provided by the sub’s caller, allowing those arguments to be changed in the sub; the copying/shifting approach prevents such changes. 3 We won’t contrast Perl subroutines with Shell user-defined functions, because functions are different in many ways, and many Shell programmers aren’t familiar with them anyway. COMPARTMENTALIZING CODE WITH SUBROUTINES 365 For those familiar with the way subroutines work in other languages, the most note- worthy aspects of Perl subroutines are these: • A subroutine’s name must be followed by parentheses, 4 even if no arguments are provided. • Subroutine definitions needn’t provide any information about their expected, or required, arguments. • All arguments to all subroutines are accessed from the array called @_. Other features of Perl’s subroutine system are natural offshoots of its sensitivity to context: • For a call in scalar context, return automatically converts an argument that’s a list variable to its corresponding scalar value. For example, return @AC_DC returns the values of that list (e.g., “AC”, “DC”) for a call in list context, but it returns that array’s number of values (2) for a call in scalar context. • A subroutine can sense the context from which it’s called 5 and tailor its return value accordingly (see “Sensing Context” in table 11.1). You’ll see all these features demonstrated in upcoming examples. But first, we’ll dis- cuss how existing code is converted to the subroutine format. 11.1.1 Defining and using subroutines Consider the script shown in listing 11.1, which centers and prints each line of its input, using code adapted from news_flash in section 8.6.1. 1 #! /usr/bin/perl -wnl 2 3 use Text::Tabs; # imports "expand" function 4 BEGIN { 5 $width=80; # or use `tput cols` to get width 6 } 7 8 # Each tab will be counted by "length" as one character, 9 # but it may act like more! 10 11 $_=expand $_; # rewrite line with tabs replaced by spaces 12 13 # Leading/trailing whitespace can make line look uncentered 14 s/^\s+//; # strip leading whitespace 15 s/\s+$//; # strip trailing whitespace 4 Assuming the programmer places sub definitions at the end of the script, which is customary. 5 Which we’ll henceforth call the caller’s context. Listing 11.1 The center script [...]... Business::UPS, for estimating shipping charges, through File::Find, for writing OS-portable programs that replace the Unix find command, to CGI (Common Gateway Interface), for generating web pages and processing web forms 12.1 CREATING MODULES With all of Perl s standard modules out there for you to use, plus the thousands available from the CPAN, why would you want to write your own modules? For several... the probability of name clashes This is especially true for components having common (and therefore clash-prone) names, such as $output, $count, and @name for variables, or calculate, validate, and get_data for subroutines For this reason, it might be best to provide IQ’s users with access to its $statistics variable—which shows the percentages of people falling into different IQ ranges—only on request,... in mind—the guideline for simple programs, which applies to the majority of the programs featured in this book: • Relax Enjoy the friendliness, power, and freedom of Perl Don’t use strict, don’t declare variables, and don’t worry—be happy! Directions for further study The following resources provide additional information on the topics covered in this chapter • man perlsub • man perlvar 22 SUMMARY #... it susceptible to name clashes and clobberations.18 However, the use of file scope, as this is called, can be appropriate for variables that aren’t storing mission-critical information. 19 18 19 380 As demonstrated with the phone_home script of section 11.2.1 File scope can also be appropriate in Perl modules, which may contain little more than variable declarations made for the benefit of their following... variable in foreach rather than accepting the convenient—but in this case troublesome— default loop variable of $_ Here’s a modified version of the foreach loop that produces the desired result, with the changes in bold: foreach $word (split){ # split line into words; store each in $word $word =~ /\b\w\w\w\w\b/ and print "Found '$word' in: '$_'\n" and last; } Now that $word is the loop variable for foreach,... how to apply these techniques for managing your own code and accessing code obtained from others Specifically, you’ll learn how to create your own modules, how to use existing modules from Perl s standard distribution, and how to locate, obtain, and install modules from the CPAN We’ll begin by showing you how to package the center_line subroutine of listing 11.3 in the form of a module We’ll end this... lack nested loops and subroutine definitions: • Relax Enjoy the friendliness, power, and freedom of Perl Don’t use strict, don’t declare variables, and don’t worry—be happy! Although this advice may sound too good to be true, it really works And you know that, because none of the dozens of Perl programs we discussed in the previous chapters needed to declare or create a special scope for a variable,... reasons, including these: • To make it easier for you to reuse your own custom code • To make it easier for you to share your custom code with others We’ll discuss how you can create your own modules next, because chances are good that you’ll need to do so at some point, and this is an easy skill to acquire The standard way for a Perl programmer to package code for easy reuse is through an approach based... the module's functionality follows sub lime { # Contents } sub stantial { # Contents } # And so forth 1; # Indicate module's end, with True value You’ll learn how to use this template next 2 For a more detailed module template that handles the needs of more complex modules, see man perlmod 390 CHAPTER 1 2 MODULES AND THE CPAN Starting the new module The italicized words in the Template’s source code... block, Main, and the END block—but not in the subroutines In contrast, examples C 17 Unfortunately, this fact has not been well documented in the Perl literature (at least, until now) VARIABLE SCOPING GUIDELINES FOR COMPLEX PROGRAMS 3 79 Variable scope A: Entire program B: BEGIN, Main, and END C: BEGIN and Main D: Main and END E: Main only use strict; use strict; use strict; use strict; use strict; BEGIN . missing from Perl . Table 10 .9 The Shell’s select loop select var ; do commands; done # Form 0 select var in LIST; do commands; done # Form 1 Ta b l e 10 . 1 0 T h e select loop for Perl use Shell::POSIX::Select; select. & description have this format in $perlpage: 8 # perlsyn Perl syntax 9 10 # Loop creates hash that maps man-page descriptions to names 11 while ( $perlpage =~ /^s+ (perl w+)s+(.+ )$/mg ) {. Shell::POSIX::Select; select () { } # Form 0 select () { CODE; } # Form 1 select (LIST) { CODE; } # Form 2 select $var (LIST) { CODE; } # Form 3 THE CPAN’S select LOOP FOR PERL 357 enclosing subroutine),