Item 9: Know common shorthands and syntax quirks.


Item 9: Know common shorthands and syntax quirks .

Perl is a "human" language in that it has a very context-dependent syntax. You can take advantage of this by omitting things that are assumed by the interpreter, for example, default arguments, $_ , and optional punctuation. Perl figures out what you really mean from the context. (Usually.)

Perl is also a very high level language with an extremely rich and diverse syntax, but sometimes the various syntactic features don't fit together as well as they might. In some cases, you may have to help Perl along by resorting to a syntactic gimmick of one kind or another. Along these lines, here are some suggestions and some things to watch out for.

Use for instead of foreach

The keyword for is actually a synonym for the keyword foreach , and vice versa. The two are completely interchangeable. Thus we have the commonly seen:

 for (<*.c>) {    $bytes += -s  } 

Really a foreach loop, adding up sizes of the .c files.

And, conversely:

 foreach ($i = 0; $i < 10; $i++) {    print $i * $i, "\n";  } 

Strange , but really a for loop, printing first 10 squares.

Perl determines which kind of loop you have written by looking at something other than the keyword. (I guess that's kind of obvious.) Substituting for in place of foreach is a fairly innocuous and frequently used shorthand among more worldly Perl programmersI've done it here and there in this book.

Swap values with list assignments

Perl doesn't have a special " swap" operator, but you can always use a list assignment to the same effect:

 ($b, $a) = ($a, $b); 

Swap $a and $b .

 ($c, $a, $b) = ($a, $b, $c); 

Rotate through $a , $b , and $c .

Slices give you a convenient syntax for permuting the contents of an array:

 @a[1, 3, 5] = @a[5, 3, 1]; 

Shuffle some elements.

 @a[map { $_ * 2 + 1, $_ * 2 }    0 .. ($#a / 2)] = @a; 

Swap odd- and even-numbered elements of @a .

Force a list context with [ ] or ( )[ ] if you have to

In some cases you may need to force an expression to be evaluated in a list context. For example, if you want to split a string captured by a regular expression memory, you might first write:

 ($str) = /([^:]*)/;  @words = split /\+/, $str; 

Split $_ on + up to : .

To write this in a single expression without the use of the temporary $str , you have to resort to trickery , because the pattern match would not return the right kind of value in the scalar context imposed by split :

 @words =    split /\+/, (/([^:]*)/)[0]; 

The inside of a literal slice is an array context, so this works.

 @words =    split /\+/, join '', /([^:]*)/; 

Another approach that works.

If you want to take a reference to a list literal in a single step, use the anonymous array constructor [ ] . The reference operator \ applied to a list literal actually creates a list of references, not a reference to a list. (Don't ask me whythis one never made much sense to me. Also see Item 32.)

 $wordlist_ref =    \(split /\+/, $str); 

WRONGcreates a scalar ref to last fragment from split .

 $wordlist_ref =    [split /\+/, $str]; 

CORRECTreturns an array ref.

Use => to make initializers, and some function calls, prettier

The => operator is a synonym for the comma operator. There is one minor difference in functionality, which is that if the left-hand argument to => is an identifier by itself, it is always treated as a string. It will not be inter-preted as a function call. Thus you can use things like print to the left of => without fear:

 @a = (time => 'flies');  print "@a\n"; 

time is taken literally.

"time flies"

 @b = (time, 'flies');  print "@b\n"; 

time operator.

"862891055 flies"

Use => to make initializers prettier, if you like. This is especially appropriate when creating initializers for hashes:

Use the => operator to beautify initializers.

 %a = (    'Ag' => 47, 'Au' => 79, 'Pt' => 78  ); 

Use arrows to pair up keys and values in hash initializers.

 %a = (    Ag => 47, Au => 79, Pt => 78  ); 

You can omit quotes around identifiers to the left (still passes strict subs ).

You can simulate named parameters for function calls. Here is one simple way to do it:

Use the => operator to simulate named parameters.

 sub img {    my %param = ( align => 'middle' );    my %param_in = @_; 

Default args.

Read params in as a hash.

 @param{keys %param_in} =      values %param_in; 

Overwrite defaults with %param_in.

 # or, just use:    # my %param = ( align => 'middle', @_ ); 

Another way to handle defaults.

 print "<img ",      (join ' ',        map { "$_=\"$param{$_}\"" }          keys %param),      ">";  } 

Write out the keys and values of the hash as an HTML tag.

 img(src => 'icon.gif', align => 'top'); 

Yields <img src="icon.gif" align="top">.

This is discussed further in Item 27.

Finally, here's another interesting use of => as syntactic sugar:

 rename "$file.c" => "$file.c.old"; 

Don't confuse => with -> , which is used for subscripting references (see Item 30) and method calls (see Item 50).

Watch what you put inside { }

Parentheses, square brackets, angle brackets, and braces all have multiple meanings in Perl. Perl uses the contents of the braces (or whatever) and the surrounding context to figure out what to do with them. Usually the result makes sense, but at times it may surprise you.

Be especially careful with braces. Braces are used to enclose blocks, delimit variable names , create anonymous hashes, and as hash element and dereferencing syntax. It's dizzying if you think about it too hard. It's pretty scary that the interpreter can tell the difference between an anonymous hash constructor and a block!

If you see a plus sign inside braces for no apparent reason, there probably is a reason for it. Perl's unary plus has no effect on its argument, but it does provide a fix for some syntactic problems:

Suppose we want to dereference a function returning an array ref.

 @a = @{func_returning_aryref}; 

WRONGrefers to variable @func_returning_aryref .

 @a = @{func_returning_aryref()}; 

OKparentheses force interpretation as a function.

 @a = @{&func_returning_aryref}; 

OKampersand forces interpretation as a function.

 @a = @{+func_returning_aryref}; 

OKanother strange use of the plus sign.

If you're unlucky, you might also run into a situation in which an anonymous hash constructor is confused with a block:

Suppose we have a function that returns a list of key-value pairs that we want to use in an anonymous hash constructor.

 $hashref = eval {    { key_value_pairs() }  }; 

WRONGinnermost set of braces looks like a block.

 $hashref = eval {    +{ key_value_pairs() }  }; 

OKit's a hash constructor when it's part of an expression.

 $hashref = eval {    return { key_value_pairs() }  }; 

OKexplicit return also makes it an expression.

And, finally, you should be aware that an identifier appearing all alone (possibly surrounded by whitespace) inside braces is taken literally as a string. [2] If it is the name of a function, the function is not called unless there is something other than just an identifier present:

[2] This is not strictly trueit applies only to braces used as part of reference or variable syntax, so "block" braces like those in while ($_ = shift @lines) { print } will work fine.

 ${shift} = 10; 

Sets $shift = 10 .

 sub soft { ${+shift} = 10; } 

Calls shift and uses it as a variable namesoft reference.

 soft 'a'; 

Sets $a = 10 .

Use @{[ ]} or eval { } to make a copy of a list

Sometimes you may want to perform a destructive operation on a copy of a list, rather than the original:

Find .h files that are missing.

 @cfiles_copy = @cfiles;  @missing_h = grep { s/\.c$/\.h/ and not -e } @cfiles_copy; 

Perl doesn't give you a function for making copies of things, but if you need to make an unnamed copy of a list, you can put the list inside the anonymous array constructor [ ] , then dereference it:

Find .h files that are missing, but without making an explicit copy.

 @missing_h = grep { s/\.c$/\.h/ and !-e } @{[@cfiles]}; 

Another way to make a copy of something is to put it inside an eval block:

 @missing_h = grep { s/\.c$/\.h/ and !-e } eval {@cfiles}; 

Use the block form of eval in situations like this, not the string form, since the block form is much more efficient (see Item 54).



Effective Perl Programming. Writing Better Programs with Perl
Effective Perl Programming: Writing Better Programs with Perl
ISBN: 0201419750
EAN: 2147483647
Year: 1996
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net