15

There's a bunch out there, as Perl is a pretty sugary language, but the most used statements in any language is the combination of if statements and setting values. I think I've found many of them, but there's still a few gaps. Ultimately, the goal would be to not have to write a variable name more than once:

Here's what I have so far:

$r ||= $s;          # $r = $s unless ($r);
$r //= $s;          # $r = $s unless (defined $r);
$r &&= $s;          # $r = $s if ($r);
$r = $c ? $s : $t;  # if ($c) { $r = $s } else { $r = $t }
$c ? $r : $s = $t;  # if ($c) { $r = $t } else { $s = $t }
$r = $s || $t;      # if ($s) { $r = $s } else { $r = $t }
$r = $s && $t;      # if ($s) { $r = $t } else { $r = $s = undef, 0, untrue, etc. }
$c and return $r;   # return $r if ($c);
$c or  return $r;   # return $r unless ($c);
$c and $r = $s;     # $r = $s if ($c);
@$r{qw(a b c d)}    # ($r->{a}, $r->{b}, $r->{c}, $r->{d})

Somebody also had a really interesting article on a "secret operator", shown here:

my @part = (
    'http://example.net/app',
    ( 'admin'  ) x!! $is_admin_link,
    ( $subsite ) x!! defined $subsite,
    $mode,
    ( $id      ) x!! defined $id,
    ( $submode ) x!! defined $submode,
);

However, what I've found to be missing from the list is:

$r <= $s;                 # read as "$r = min($r, $s);" except with short-circuiting
$r = $s if (defined $s);  # what's the opposite of //?
$r and return $r          # can that be done without repeating $r?

Is there anything else worth adding? What other conditional set variables are available to reduce the code? What else is missing?

SineSwiper
  • 2,046
  • 2
  • 18
  • 22
  • `$c && $r = $s` needs a little more sugar (you have to say `($r=$s)`) – mob Oct 17 '11 at 16:18
  • @mob, I'm afraid that's rather bitter taste for the OP :-) – Tomas Oct 17 '11 at 16:29
  • `$r = !$s ? $s : $t` is the same thing as `$r = $s && $t` – mob Oct 17 '11 at 16:39
  • 1
    `min($r, $s)` can't be short-circuited. Every element must be checked in order to find the smallest one. – ikegami Oct 17 '11 at 17:38
  • @ikegami: True, but it's more operations. For example, the standard min is going to do the following: read $r, read $s, put into a $list, cycle and find the lowest value, store into $r. When all I need it to do is: compare $r with $s, *STOP* or store $s in $r. That's a short-circuit. – SineSwiper Oct 17 '11 at 17:54

5 Answers5

8

These structures from your question could be written a little bit more clearly using the low precedence and and or keywords:

$c and return $r;    # return $r if ($c);
$c or return $r;     # return $r unless ($c);
$c and $r = $s;      # $r = $s if ($c);

The nice thing about and and or is that unlike the statement modifier control words, and and or can be chained into compound expressions.

Another useful tool for syntactic sugar is using the for/foreach loop as a topicalizer over a single value. Consider the following:

$var = $new_value if defined $new_value;

vs

defined and $var = $_ for $new_value;

or things like:

$foo = "[$foo]";
$bar = "[$bar]";

$_ = "[$_]" for $foo, $bar;

the map function can also be used in this manner, and has a return value you can use.

Eric Strom
  • 39,821
  • 2
  • 80
  • 152
  • Oh, I forgot about the and/or words. And yeah, I use map very often for definitions like `my ($id, $name) = map { $DEV->{$d}{$_} } qw(hostid hostname);` – SineSwiper Oct 18 '11 at 18:52
  • don't forget slices: `my ($id, $name) = @{$$DEV{$d}}{qw(hostid hostname)};` – Eric Strom Oct 18 '11 at 21:03
6

There's also the left hand side ternary operator:

$cond ? $var1 : $var2 = "the value";

is equivalent to:

if ($cond) {
    $var1 = "the value";
} else {
    $var2 = "the value";
}
Toto
  • 89,455
  • 62
  • 89
  • 125
3

$r = $r < $s ? $r : $s;:

    $r = $s if $r > $s;

or

    use List::Util qw( min );

    $r = min($r, $s);

or:

    sub min_inplace {
       my $min_ref = \shift;
       for (@_) { $$min_ref = $_ if $$min_ref > $_; }
    }

    min_inplace($r, $s);

$r = $s if (defined $s);:

    $r = $s // $r;

$r = $t; $r = $s if (defined $s);:

    $r = $s // $t;

$r = !$s ? $s : $t;:

    $r = $s && $t;
ikegami
  • 367,544
  • 15
  • 269
  • 518
1

One of the biggest called for features in Perl was the switch statement. This finally appeared in Perl 5.10. I'm just using the example from the documentation:

use feature qw(say switch);  #My preference
#use feature ":5.10";        #This does both "say" and "switch"

[...]

given($foo) {
    when (undef) {
        say '$foo is undefined';
    }
    when ("foo") {
        say '$foo is the string "foo"';
    }
    when ([1,3,5,7,9]) {
        say '$foo is an odd digit';
        continue; # Fall through
    }
    when ($_ < 100) {
        say '$foo is numerically less than 100';
    }
    when (\&complicated_check) {
        say 'a complicated check for $foo is true';
    }
    default {
        die q(I don't know what to do with $foo);
    }
}

Why o' why did they go with given/when and not switch/case like you find in most languages is a mystery to me. And, why if the statement is given/when, do you specify it in use features as switch?

Alas, the people who made these decisions are at a higher plane than I am, so I have no right to even question these luminaries.


I avoid the more exotic stuff, and stick with the easiest to understand syntax. Imagine the person who has to go through your code and find a bug of add a feature, which would be easier for that person to understand:

$r &&= $s;

or

if ($r) {
    $r = $s;
}

And, maybe I might realize that I really meant:

if (not defined $r) {
    $r = $s;
}

And, in this case, I might even say:

$r = $s if not defined $r;

Although I don't usually like post-fixed if statements because people tend to miss the if part when glancing through the code.

Perl is compiled at runtime, and the compiler is fairly efficient. So, even though it's way cooler to write $r &&= $s and it earns it earns you more geek points and is less to type, it doesn't execute any faster. The biggest amount of time spent on code is on maintaining it, so I'd rather skip the fancy stuff and go for readability.

By the way, when I think of syntactic sugar, I think of things added to the language to improve readability. A great example is the -> operator:

${${${$employee_ref}[0]}{phone}}[0];

vs.

$employee_ref->[0]->{phone}->[0];

Of course, if you're storing data as a reference to a list to a hash to a list, you are probably better off using object oriented coding.

David W.
  • 105,218
  • 39
  • 216
  • 337
  • To answer your question about use features, just "use common::sense;" Best module ever invented! As far as readability, it's only as readable as the person who may or may not understand it. For example, somebody might not know what `$a || die` really does, but it's used everywhere. People tend to shy away from the ~~ smart matching operator because of this, but all is required is looking up the reference. It's worth the effort to do something as simple as `@q ~~ $val`, instead of bothering with a first, or for loop, or anything else. – SineSwiper Oct 17 '11 at 17:46
  • given/when have such names because they were ported from Perl 6. And in Perl 6 they were renamed because they do much more than usual switch/case – Alexandr Ciornii Oct 17 '11 at 20:12
  • I dunno about you, but I like to think of `$$employee_ref[0]{phone}[0]` :) – Eric Strom Oct 20 '11 at 01:26
0

There's also:

$hash{$key||'foo'} = 1;  # if($key) { $hash{$key} = 1 } else { $hash{'foo'} = 1 }
MisterEd
  • 1,725
  • 1
  • 14
  • 15