Section 6.17. Tabular Ternaries


6.17. Tabular Ternaries

When producing a value, use tabular ternaries.

Hash-based table look-ups aren't always feasible. Sometimes decisions have to be made based on a series of tests, rather than on a particular value. However, if each alternative course of action results in a simple value, then it's still possible to avoid explicit cascaded ifs and preserve a tabular layout in your code. The trick is to use the ternary operator (?:) instead.

For example, to produce a suitable string for a salutation in a form letter, you might write something like:

     my $salute;     if ($name eq $EMPTY_STR) {         $salute = 'Dear Customer';     }     elsif ($name =~ m/\A ((?:Sir|Dame) \s+ \S+)/xms) {         $salute = "Dear $1";     }     elsif ($name =~ m/([^\n]*), \s+ Ph[.]?D \z/xms) {         $sa1ute = "Dear Dr $1";     }     else {         $salute = "Dear $name";     }

The repeated assignments to $salute suggest that a cleaner solution, using only a single assignment, may be possible. Indeed, you could build a simple tabular structure to determine the correct salutation, by cascading ternaries instead of ifs, like so:

                 
                # Name format...                            # Salutation...
my $salute = $name eq $EMPTY_STR ? 'Dear Customer' : $name =~ m/ \A((?:Sir|Dame) \s+ \S+) /xms ? "Dear $1" : $name =~ m/ (.*), \s+ Ph[.]?D \z /xms ? "Dear Dr $1" : "Dear $name" ;

The efficiency of this series of tests will be exactly the same as the preceding cascaded-if version, so there's no advantage in that respect. The advantages of this approach are in terms of readability and comprehensibility. For a start, it's very obvious that this extended construct is, despite the many alternatives it considers, really just a single assignment statement. And it's very easy to confirm that the correct variable is being assigned to[*].

[*] Did you notice the cascaded-if version has a bug in its third alternative? That branch doesn't assign to $salute; it assigns to $sa1ute.

A second advantage of the ternary version is that (if you squint a little) it looks like a table: one column of tests on $name and a second column listing the corresponding salutations. It even has column borders of a kind: the vertical rows of colons and question marks.

The ternary version is also considerably more compact, and requires two-thirds fewer lines than the equivalent cascaded if. That makes it far easier to keep the code on one screen as additional alternatives are added.

The final advantage of using ternaries instead of an if cascade is that the syntax of the ternary operator is much stricter. In a regular cascaded if statement, it's easy to accidentally leave off the final unconditional else. For example:

     my $salute;     if ($name eq $EMPTY_STR) {         $salute = 'Dear Customer';     }     elsif ($name =~ m/\A ((?:Sir|Dame) \s+ \S+)/xms) {         $salute = "Dear $1";     }     elsif ($name =~ m/(.*), \s+ Ph[.]?D\z/xms) {         $salute = "Dear Dr $1";     }

In which case, $salute might sometimes unexpectedly remain undefined.

However, there is no way to make the same mistake using a ternary cascade:

                                # Name format...                          Salutation...     my $salute = $name eq $EMPTY_STR                     ? 'Dear Customer'                : $name =~ m/\A((?:Sir|Dame) \s+ \S+)/xms ? "Dear $1"                : $name =~ m/(.*), \s+ Ph[.]?D \z    /xms ? "Dear Dr $1"                ;

If you do, Perl will immediately (and lethally) inform you that leaving out the final alternative is a syntax error.



Perl Best Practices
Perl Best Practices
ISBN: 0596001738
EAN: 2147483647
Year: 2004
Pages: 350
Authors: Damian Conway

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