Perl Best Practices [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Perl Best Practices [Electronic resources] - نسخه متنی

Damian Conway

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید







6.4. Negative Control Statements


Don't use unless or until at all .


Perl is unusual amongst programming languages in that it provides not only positive conditional tests (if and while), but also their negative counterparts (unless and until). Some people find that these keywords can make certain control structures read more naturally to them:


RANGE_CHECK:
until ($measurement > $ACCEPTANCE_THRESHOLD) {
$measurement = get_next_measurement( );
redo RANGE_CHECK unless defined $measurement;
# etc.
}

However, for many other developers, the relative unfamiliarity of these negated tests actually makes the resulting code harder to read than the equivalent "positive" version:


RANGE_CHECK:
while ($measurement <= $ACCEPTANCE_THRESHOLD) {
$measurement = get_next_measurement( );
redo RANGE_CHECK if !defined $measurement;

# etc.

}

More importantly, the negative tests don't scale well. They almost always become much harder to comprehend as soon as their condition has two or more components, especially if any of those components is itself expressed negatively. For example, most people have significantly more difficulty understanding the double negatives in:


VALIDITY_CHECK:
until ($measurement > $ACCEPTANCE_THRESHOLD && ! $is_exception{$measurement}) {
$measurement = get_next_measurement( );
redo VALIDITY_CHECK unless defined $measurement && $measurement ne '[null]';
# etc.
}

So unless and until are inherently harder to maintain. In particular, whenever a negative control statement is extended to include a negative operator, it will have to be re-cast as a positive control, which requires you to change both the keyword and the conditional:


VALIDITY_CHECK:
while ($measurement < $ACCEPTANCE_THRESHOLD && $is_exception{$measurement}) {
$measurement = get_next_measurement( );
redo VALIDITY_CHECK if !defined $measurement || $measurement eq '[null]';
# etc.
}

Not only is that extra effort, it's error-prone effort. Reworking conditionals in that way is an excellent opportunity to introduce new and subtle bugs into your program...just as the previous example did. The original until condition was:


until ($measurement > $ACCEPTANCE_THRESHOLD && ! $is_exception{$measurement}) {

The corresponding "positive" version

should have been:


while ($measurement <= $ACCEPTANCE_THRESHOLD || $is_exception{$measurement}) {

This kind of mistake is unfortunately common, but very easy to avoid. If you never use an unless or until, then you'll never have to rewrite it as an if or while. Moreover, if your control statements are always "positive", your code will generally be more comprehensible to all readers[*], regardless of the complexity of its logical expressions. Even in the simple cases, something like:

[*] Including yourself in six months' time . . .



croak "$value is missing" if !$found;

is not appreciably harder to comprehend than:


croak "$value is missing" unless $found;

unless or until are minor conveniences that can easily become major liabilities. They're never necessary, and frequently counterproductive. Don't use them.


Don't Fail Not to Use Chained Negatives


The "Negative Control Statements" guideline has proved to be one of the most controversial in this book. Though few people will miss until, many very good Perl programmers feel that unless is a valid choice, which improves readability in a limited number of circumstances.

If you, or your team, are persuaded by such arguments and decide to allow the use of unless as part of your coding practices, then make sure that you only ever use it in ways that genuinely improve the readability of your code. The best way to ensure that is to require that the conditional expression of any negative control statement must always itself be expressed using "positive" logic.

That is,

never write an unless (or an until) in which the condition includes a "negative" operator, an inequality, or any kind of ordering test. Don't use any of the following operators with an unless or an until:


! not # Negation of value
!~ != ne # Inequality of value
< > <= >= <=> # Numerical ordering
lt gt le ge cmp # String ordering

Abiding by that rule would permit the following limited uses:


next NAME unless $name eq 'Harry';
redo NAME unless $name =~ m/Mud{1,2}/xms;
unless ($count == $MAX_COUNT) {
carp 'Search terminated prematurely';
}

but would still prohibit confusing double-negatives and reversed inequalities like:


last NAME unless $name ne 'Harry';
exit $FAIL unless $name !~ m/Mud{1,2}/xms;
unless ($count != $MAX_COUNT) {
carp "Still searching...\n";
}
return $count
unless $count < 10 || $tries >= $MAX_TRIES;
until ($input ne $EMPTY_STR) {
$input = prompt '> ';
}

Remember too that, if you do choose to allow unless or until, exTRa attention will be required whenever a conditional expression is extended. In particular, if the extension adds a "negative" of any kind, then you will need to convert the negative control statement to the appropriate positive form, and rewrite of the original condition accordingly.

The added degree of complexity and awareness that this transformation always requires and the risk of introducing errors that it inevitably involves are two good reasons to reread the "Negative Control Statements" guideline, and to reconsider banning unless and until altogether.


/ 317