Categories
Posts in this category
- Introduction
- Strings, Arrays, Hashes;
- Types
- Basic Control Structures
- Subroutines and Signatures
- Objects and Classes
- Contexts
- Regexes (also called "rules")
- Junctions
- Comparing and Matching
- Containers and Values
- Where we are now - an update
- Changes to Perl 5 Operators
- Laziness
- Custom Operators
- The MAIN sub
- Twigils
- Enums
- Unicode
- Scoping
- Regexes strike back
- A grammar for (pseudo) XML
- Subset Types
- The State of the implementations
- Quoting and Parsing
- The Reduction Meta Operator
- The Cross Meta Operator
- Exceptions and control exceptions
- Common Perl 6 data processing idioms
- Currying
Wed, 10 Dec 2008
The Reduction Meta Operator
Permanent link
NAME
"Perl 5 to 6" Lesson 24 - The Reduction Meta Operator
SYNOPSIS
say [+] 1, 2, 3; # 6 say [+] (); # 0 say [~] <a b>; # ab say [**] 2, 3, 4; # 2417851639229258349412352 [\+] 1, 2, 3, 4 # 1, 3, 6, 10 [\**] 2, 3, 4 # 4, 81, 2417851639229258349412352 if [<=] @list { say "ascending order"; }
Description
The reduction meta operator [...]
can enclose any associative infix operator, and turn it into a list operator. This happens as if the operator was just put between the items of the list, so [op] $i1, $i2, @rest
returns the same result as if it was written as $i1 op $i2 op @rest[0] op @rest[1] ...
.
This is a very powerful construct that promotes the plus +
operator into a sum
function, ~
into a join
(with empty separator) and so on. It is somewhat similar to the List.reduce
function, and if you had some exposure to functional programming, you'll probably know about foldl
and foldr
(in Lisp or Haskell). Unlike those, [...]
respects the associativity of the enclosed operator, so [/] 1, 2, 3
is interpreted as (1 / 2) / 3
(left associative), [**] 1, 2, 3
is handled correctly as 1 ** (2**3)
(right associative).
Like all other operators, whitespace are forbidden, so you while you can write [+]
, you can't say [ + ]
. (This also helps to disambiguate it from array literals).
Since comparison operators can be chained, you can also write things like
if [==] @nums { say "all nums in @nums are the same" } elsif [<] @nums { say "@nums is in strict ascending order" } elsif [<=] @nums { say "@nums is in ascending order"}
However you cannot reduce the assignment operator:
my @a = 1..3; [=] @a, 4; # Cannot reduce with = because list assignment operators are too fiddly
Getting partial results
There's a special form of this operator that uses a backslash like this: [\+]
. It returns a list of the partial evaluation results. So [\+] 1..3
returns the list 1, 1+2, 1+2+3
, which is of course 1, 3, 6
.
[\~] 'a' .. 'd' # <a ab abc abcd>
Since right-associative operators evaluate from right to left, you also get the partial results that way:
[\**] 1..3; # 3, 2**3, 1**(2**3), which is 3, 8, 1
Multiple reduction operators can be combined:
[~] [\**] 1..3; # "381"
MOTIVATION
Programmers are lazy, and don't want to write a loop just to apply a binary operator to all elements of a list. List.reduce
does something similar, but it's not as terse as the meta operator ([+] @list
would be @list.reduce(&infix:<+>)
). Also with reduce you have to takes care of the associativity of the operator yourself, whereas the meta operator handles it for you.
If you're not convinced, play a bit with it (rakudo implements it), it's real fun.
SEE ALSO
http://design.perl6.org/S03.html#Reduction_operators, http://www.perlmonks.org/?node_id=716497