views:

332

answers:

7

I am currently documenting all of Perl 5's operators (see the perlopref GitHub project) and I have decided to include Perl 5's pseudo-operators as well. To me, a pseudo-operator in Perl is anything that looks like an operator, but is really more than one operator or a some other piece of syntax. I have documented the four I am familiar with already:

  • ()= the countof operator
  • =()= the goatse/countof operator
  • ~~ the scalar context operator
  • }{ the Eskimo-kiss operator

What other names exist for these pseudo-operators, and do you know of any pseudo-operators I have missed?

=head1 Pseudo-operators

There are idioms in Perl 5 that appear to be operators, but are really a
combination of several operators or pieces of syntax. These pseudo-operators
have the precedence of the constituent parts.

=head2 ()= X

=head3 Description

This pseudo-operator is the list assignment operator (aka the countof
operator).  It is made up of two items C<()>, and C<=>.  In scalar context
it returns the number of items in the list X.  In list context it returns an
empty list.  It is useful when you have something that returns a list and
you want to know the number of items in that list and don't care about the
list's contents.  It is needed because the comma operator returns the last
item in the sequence rather than the number of items in the sequence when it
is placed in scalar context.

It works because the assignment operator returns the number of items
available to be assigned when its left hand side has list context.  In the
following example there are five values in the list being assigned to the
list C<($x, $y, $z)>, so C<$count> is assigned C<5>.

    my $count = my ($x, $y, $z) = qw/a b c d e/;

The empty list (the C<()> part of the pseudo-operator) triggers this
behavior.

=head3 Example

    sub f { return qw/a b c d e/ }

    my $count = ()= f();              #$count is now 5

    my $string = "cat cat dog cat";

    my $cats = ()= $string =~ /cat/g; #$cats is now 3

    print scalar( ()= f() ), "\n";    #prints "5\n"

=head3 See also

L</X = Y> and L</X =()= Y>

=head2 X =()= Y

This pseudo-operator is often called the goatse operator for reasons better
left unexamined; it is also called the list assignment or countof operator.
It is made up of three items C<=>, C<()>, and C<=>.  When X is a scalar
variable, the number of items in the list Y is returned.  If X is an array
or a hash it it returns an empty list.  It is useful when you have something
that returns a list and you want to know the number of items in that list
and don't care about the list's contents.  It is needed because the comma
operator returns the last item in the sequence rather than the number of
items in the sequence when it is placed in scalar context.

It works because the assignment operator returns the number of items
available to be assigned when its left hand side has list context.  In the
following example there are five values in the list being assigned to the
list C<($x, $y, $z)>, so C<$count> is assigned C<5>.

    my $count = my ($x, $y, $z) = qw/a b c d e/;

The empty list (the C<()> part of the pseudo-operator) triggers this
behavior.

=head3 Example

    sub f { return qw/a b c d e/ }

    my $count =()= f();              #$count is now 5

    my $string = "cat cat dog cat";

    my $cats =()= $string =~ /cat/g; #$cats is now 3

=head3 See also

L</=> and L</()=>

=head2 ~~X

=head3 Description

This pseudo-operator is named the scalar context operator.  It is made up of
two bitwise negation operators.  It provides scalar context to the
expression X.  It works because the first bitwise negation operator provides
scalar context to X and performs a bitwise negation of the result; since the
result of two bitwise negations is the original item, the value of the
original expression is preserved.

With the addition of the Smart match operator, this pseudo-operator is even
more confusing.  The C<scalar> function is much easier to understand and you
are encouraged to use it instead.

=head3 Example

    my @a = qw/a b c d/;

    print ~~@a, "\n"; #prints 4

=head3 See also

L</~X>, L</X ~~ Y>, and L<perlfunc/scalar>

=head2 X }{ Y

=head3 Description

This pseudo-operator is called the Eskimo-kiss operator because it looks
like two faces touching noses.  It is made up of an closing brace and an
opening brace.  It is used when using C<perl> as a command-line program with
the C<-n> or C<-p> options.  It has the effect of running X inside of the
loop created by C<-n> or C<-p> and running Y at the end of the program.  It
works because the closing brace closes the loop created by C<-n> or C<-p>
and the opening brace creates a new bare block that is closed by the loop's
original ending.  You can see this behavior by using the L<B::Deparse>
module.  Here is the command C<perl -ne 'print $_;'> deparsed:

    LINE: while (defined($_ = <ARGV>)) {
        print $_;
    }

Notice how the original code was wrapped with the C<while> loop.  Here is
the deparsing of C<perl -ne '$count++ if /foo/; }{ print "$count\n"'>:

    LINE: while (defined($_ = <ARGV>)) {
        ++$count if /foo/;
    }
    {
        print "$count\n";
    }

Notice how the C<while> loop is closed by the closing brace we added and the
opening brace starts a new bare block that is closed by the closing brace
that was originally intended to close the C<while> loop.

=head3 Example

    # count unique lines in the file FOO
    perl -nle '$seen{$_}++ }{ print "$_ => $seen{$_}" for keys %seen' FOO

    # sum all of the lines until the user types control-d
    perl -nle '$sum += $_ }{ print $sum'

=head3 See also

L<perlrun> and L<perlsyn>

=cut
+13  A: 

Nice project, here are a few:

scalar x!! $value    # conditional scalar include operator
(list) x!! $value    # conditional list include operator
'string' x/pattern/  # conditional include if pattern
"@{[ list ]}"        # interpolate list expression operator
"${\scalar}"         # interpolate scalar expression operator
!! $scalar           # scalar -> boolean operator
+0                   # cast to numeric operator
.''                  # cast to string operator

{ ($value or next)->depends_on_value() }  # early bail out operator
# aka using next/last/redo with bare blocks to avoid duplicate variable lookups
# might be a stretch to call this an operator though...

sub{\@_}->( list )   # list capture "operator", like [ list ] but with aliases
Eric Strom
I love the `x!!` trick, but usually forget about it when I actually need it, and end up doing a `( $cond ? $foo : ( ) )` instead.
friedo
Crap, I forgot all about the !! bool op. I don't know if +0 and .'' really count, but I guess there is no harm in adding them. The interpolation tricks are a definite include. I had never considered using the repetition operator that way, those are definite adds as well. Thanks.
Chas. Owens
It really says something about my mental state when I can write "It is occasionally used in pairs (!!) to convert any false value to "" and any true value to 1." in the section on high-precedence logical negation operator, but forget about it completely when writing the section on pseudo-operators. Perhaps I should reread my document.
Chas. Owens
Hmm! I'm torn between +1 for an overall post and -1 for the "maintenance nightmare" aura emanating from the "early bail out" operator.
DVK
@DVK give him the +1, we aren't encouraging this behavior, just documenting it so when you run into it you have a shot at understanding it.
Chas. Owens
@Chas. - OK, I'm game for +1, especially if you'd be so kind as to explicitly include "Don't do this, dude! Seriously! Bad Karma!" in the POD :)
DVK
... I have enough trouble convincing skeptics on SO that one can write good quality maintainable readable Perl code without people actively trying to sabotage that by promoting code golphish idioms :) ;)
DVK
I already added such a section earlier today, it reads "N.B. There are often better ways of doing all of these things and those ways will be easier to understand for the people who will maintain your code after you."
Chas. Owens
+1  A: 

You have two "countof" (pseudo-)operators, and I don't really see the difference between them.

From the examples of "the countof operator":

my $count = ()= f();              #$count is now 5
my $string = "cat cat dog cat";
my $cats = ()= $string =~ /cat/g; #$cats is now 3

From the examples of "the goatse/countof operator":

my $count =()= f();              #$count is now 5
my $string = "cat cat dog cat";
my $cats =()= $string =~ /cat/g; #$cats is now 3

Both sets of examples are identical, modulo whitespace. What is your reasoning for considering them to be two distinct pseudo-operators?

Dave Sherohman
Because some people write it one way and others a different way. And the examples are not completely identical, you left out `print scalar( ()= f() ), "\n"; #prints "5\n"` which can't be done with `=()=`.
Chas. Owens
@Chas.: Yeah, I skipped that one, but took the reasoning as obvious: `()=` is what does the actual work. The leading `=` on the full goatse just assigns the result generated by `()=` to a variable, which you're not doing in the function call. Saying they're different operators just because some people include whitespace and others don't is like saying that `1 + 1 = 2` and `1+1=2` are different expressions, using different `+` and `=` operators, "*because some people write it one way and others a different way*".
Dave Sherohman
@Dave Sherohman I would agree with you except for the fact that they look like different operators and have different descriptions. Imagine you don't know how to parse `my $x =()= /a/g;`, would you rather `=()=` be documented separately or would you like to try to figure out that it is documented under `()=`? The initial reason this document was started was that a newbie could find the docs for `||=` in perlop.
Chas. Owens
+3  A: 

In Perl these are generally referred to as "secret operators".

A partial list of "secret operators" can be had here. The best and most complete list is probably in possession of Philippe Bruhad aka BooK and his Secret Perl Operators talk but I don't know where its available. You might ask him. You can probably glean some more from Obfuscation, Golf and Secret Operators.

Schwern
Hmm, I don't know if it is worth documenting all of them in this way (I am frankly exhausted just trying to get the normal ops documented individually). Right now I am giving priority to the ones I would expect to see in non-obfu code.
Chas. Owens
+3  A: 

Don't forget the Flaming X-Wing =<>=~.

The Fun With Perl mailing list will prove useful for your research.

Schwern
+2  A: 

The "goes to" and "is approached by" operators:

$x = 10;
say $x while $x --> 4;
# prints 9 through 4

$x = 10;
say $x while 4 <-- $x;
# prints 9 through 5

They're not unique to Perl.

Rob Kennedy
While the look really special, these things are an awesome Way to encourage Obiwan errors... :)
tsee
I found the SO discussion of this operator very entertaining: [What is the name of this operator: “-->”?](http://stackoverflow.com/questions/1642028/what-is-the-name-of-this-operator)
Ether
Yep, I have definitely seen people use these before.
Chas. Owens
+2  A: 

From this question, I discovered the %{{}} operator to cast a list as a hash. Useful in contexts where a hash argument (and not a hash assignment) are required.

@list = (a,1,b,2);
print values @list;        # arg 1 to values must be hash (not array dereference)
print values %{@list}      # prints nothing
print values (%temp=@list) # arg 1 to values must be hash (not list assignment)
print values %{{@list}}    # success: prints 12

If @list does not contain any duplicate keys (odd-elements), this operator also provides a way to access the odd or even elements of a list:

@even_elements = keys %{{@list}}     # @list[0,2,4,...]
@odd_elements = values %{{@list}}    # @list[1,3,5,...]
mobrule
This also allows direct access access to elements of a hash returned by a function (with ${{sub_name()}}: sub foo { return (a => 1, b => 2, c => 3) } print ${{foo}}{b};
MkV
+1  A: 

How about the "Boolean one-or-zero" operator: 1&!!

For example:

my %result_of = (
    " 1&!! '0 but true' " => 1&!! '0 but true',
    " 1&!! '0'          " => 1&!! '0',
    " 1&!! 'text'       " => 1&!! 'text',
    " 1&!! 0            " => 1&!! 0,
    " 1&!! 1            " => 1&!! 1,
    " 1&!! undef        " => 1&!! undef,
);

for my $expression ( sort keys %result_of){
    print "$expression = " . $result_of{$expression} . "\n";
}

gives the following output:

 1&!! '0 but true'  = 1
 1&!! '0'           = 0
 1&!! 'text'        = 1
 1&!! 0             = 0
 1&!! 1             = 1
 1&!! undef         = 0
molecules