Assigning using ternary operator?

2019-03-09 18:44发布

I am on Perl 5.8 and am needing to assign a default value. I ended up doing this:

if ($model->test) {
    $review = "1"
} else {
    $review = ''
}

The value of $model->test is going to be either "1" or undefined. If there's something in $model->test, set $review to "1" otherwise set it equal to ''.

Because it's not Perl 5.10 I can't use the new swanky defined-or operator. My first reaction was to use the ternary operator like this...

defined($model->test) ? $review = "1" : $review = '';

but that didn't work either.

Does anyone have an idea how to assign this more efficiently? Janie

7条回答
Juvenile、少年°
2楼-- · 2019-03-09 18:54

You have a precedence problem. What you have is the same as

( defined($model->test) ? $review="1" : $review ) = '';

You could make it work with parens.

my $review; $model->test ? ( $review='1' ) : ( $review='' );

But it's much cleaner to move the assignment out.

my $review = $model->test ? '1' : '';

Of course, you could simply use

my $review = $model->test || '';

But why change undef to an empty string?

my $review = $model->test;
查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-03-09 18:57

First of all, "That didn't work either" is not the most helpful thing you could tell us. It's important to know exactly how it didn't work: what did it do, what did you expect, and how to they differ?

But the problem with

defined($model->test) ? $review="1" : $review='';

is operator precedence. The conditional operator ? : binds more tightly than the assignment operator =, so the above is equivalent to:

(defined($model->test) ? $review="1" : $review) = '';

So if $model->test is defined, it does the equivalent of

$review = "1" = '';

You can fix that problem with parentheses:

defined($model->test) ? ($review="1") : ($review='');

But really, why would you want to? The conditional (ternary) operator is useful when you want to use the result. If the result is going to be discarded, as it is here, it's clearer (and, as you've seen, less error-prone) to use an if/else statement:

if (defined($model->test) {
    $review = "1";
}
else {
    $review = "";
}

or, if you insist on writing it on one line:

if (defined($model->test) { $review = "1"; } else { $review = ""; }

If you really want to use a conditional expression, you can do this:

$review = defined($model->test) ? "1" : "";

which is probably a reasonable way to do it.

BUT :

The defined operator itself yields either "1" (true) or "" (false). so the whole thing can be reduced to:

$review = defined($model->test);
查看更多
老娘就宠你
4楼-- · 2019-03-09 18:58

Besides the conditional operator, I often like to use do, which returns the value from the last evaluated expression:

my $review = do {
     if( ... ) { 'foo' }
  elsif( ... ) { 'bar' }
  elsif( ... ) { 'baz' }
  else         { 'defaut' }
  };
查看更多
Summer. ? 凉城
5楼-- · 2019-03-09 19:02

I assume that $model->test is supposed to return a true or false value.

Unless it specifically states that the false value is undef, the method could be rewritten to start returning some other false value instead. Which would break anything that only checks if the value is defined.
( I think it is a bug that the method returns undef instead of the canonical false value. )

So the best way to set $review is to test the truthfulness of the returned value; not it's definedness.

my $review = $model->test ? 1 : '';

I would like to point out that this still has a bug in it. If you want to be able to use the value as a number, it will emit warnings if it was false.

To fix that you should return !1 (canonical false value), which will return a value that is the string '', but also has the numerical value of 0.

my $review = $model->test ? 1 : !1;

Notice that it could be simplified to just:

my $review = !! $model->test; # invert it twice

If you only want to change the value only when it is false, you could use the or operator ||.

my $review = $model->test || !1;

If you really only want to know if it is defined, or not why don't you just use defined.

my $review = defined $model->test;

If you want to change the value only when it is undefined, and you have Perl 5.10 or newer you could use the defined-or operator (//).

my $review = $model->test // !1;

On an older Perl, that would require more than one statement.

my $review = $model->test;
$review = !1 unless defined $review;
查看更多
欢心
6楼-- · 2019-03-09 19:07

I'd usually write this as:

$review = ( defined($model->test) ? 1 : '' );

where the parentheses are for clarity for other people reading the code.

查看更多
手持菜刀,她持情操
7楼-- · 2019-03-09 19:11

$model->test is going to be either "1" or undefined. If there's something in $model->test, set $review to "1" otherwise set it ''

Then just use this:

$review = $model->test || "";
查看更多
登录 后发表回答