List::Util
NAME
Raku port of Perl's List::Util module 1.49
SYNOPSIS
use List::Util <
reduce any all none notall first
max maxstr min minstr product sum sum0
pairs unpairs pairkeys pairvalues pairfirst pairgrep pairmap
shuffle uniq uniqnum uniqstr
>;
DESCRIPTION
This module tries to mimic the behaviour of Perl's List::Util
module as closely as possible in the Raku Programming Language.
List::Util
contains a selection of subroutines that people have expressed would be nice to have in the perl 5 core, but the usage would not really be high enough to warrant the use of a keyword, and the size so small such that being individual extensions would be wasteful.
By default List::Util
does not export any subroutines.
Porting Caveats
Raku does not have the concept of scalar
and list
context. Usually, the effect of a scalar context can be achieved by prefixing +
to the result, which would effectively return the number of elements in the result, which usually is the same as the scalar context of Raku of these functions.
Raku does not have a magic $a
and $b
. But they can be made to exist by specifying the correct signature to blocks, specifically "-> b". These have been used in all examples that needed them. Just using the signature auto-generating $^a
and$^b
would be more Raku like. But since we want to keep the documentation as close to the original as possible, it was decided to specifically specify the "-> b" signatures.
Raku also doesn't have a single undef
value, but instead has Type Objects
, which could be considered undef values, but with a type annotation.
Raku has real Pair
objects, which in the Perl version are mimiced by blessed arrays that have a .key
and .value
methods. In the Raku version these are represented by a subclass of the List
class, namely the P5Pair
, which also provides a .key and a .value method.
Also note there are no special parsing rules with regards to blocks in Raku. So a comma is always required after having specified a block.
The following functions are actually built-ins in Raku.
reduce any all none first max min sum uniq
They mostly provide the same or similar semantics, but there may be subtle differences, so it was decided to not just use the built-ins. If these functions are imported from this library in a scope, they will used instead of the Raku builtins. The easiest way to use both the functions of this library and the raku builtins in the same scope, is to use the method syntax for the Raku versions.
{ # Note: imports in Raku are always lexically scoped
use List::Util <max>;
say max 1..10; # Ported Perl version
say (1..10).max; # Raku version
}
say max 1..10; # Raku version again
LIST-REDUCTION FUNCTIONS
The following set of functions all reduce a list down to a single value.
reduce
$result = reduce -> $a, $b { BLOCK }, @list;
Reduces @list
by calling BLOCK
multiple times, setting $a
and $b
each time. The first call will be with $a
and $b
set to the first two elements of the list, subsequent calls will be done by setting $a
to the result of the previous call and $b
to the next element in the list.
Returns the result of the last call to the BLOCK
. If @list
is empty then Nil
is returned. If @list
only contains one element then that element is returned and BLOCK
is not executed.
The following examples all demonstrate how reduce
could be used to implement the other list-reduction functions in this module. (They are not in fact implemented like this, but instead in a more efficient manner in individual C functions).
$foo = reduce -> $a, $b { defined($a) ? $a :
$code->(local $_ = $b) ? $b :
Nil } Nil, @list; # first
$foo = reduce -> $a, $b { $a > $b ?? $a !! $b } 1..10; # max
$foo = reduce -> $a, $b { $a gt $b ?? $a !! $b } 'A'..'Z'; # maxstr
$foo = reduce -> $a, $b { $a < $b ?? $a !! $b } 1..10; # min
$foo = reduce -> $a, $b { $a lt $b ?? $a !! $b } 'aa'..'zz'; # minstr
$foo = reduce -> $a, $b { $a + $b } 1 .. 10; # sum
$foo = reduce -> $a, $b { $a ~ $b } @bar; # concat
$foo = reduce -> $a, $b { $a || $b } 0, @bar; # any
$foo = reduce -> $a, $b { $a && $b } 1, @bar; # all
$foo = reduce -> $a, $b { $a && !$b } 1, @bar; # none
$foo = reduce -> $a, $b { $a || !$b) } 0, @bar; # notall
# Note that these implementations do not fully short-circuit
If your algorithm requires that reduce
produce an identity value, then make sure that you always pass that identity value as the first argument to prevent Nil
being returned
$foo = reduce -> $a, $b { $a + $b } 0, @values; # sum with 0 identity value
The above example code blocks also suggest how to use reduce
to build a more efficient combined version of one of these basic functions and a map
block. For example, to find the total length of all the strings in a list, we could use
$total = sum map { .chars }, @strings;
However, this produces a list of temporary integer values as long as the original list of strings, only to reduce it down to a single value again. We can compute the same result more efficiently by using reduce
with a code block that accumulates lengths by writing this instead as:
$total = reduce -> $a, $b { $a + $b.chars } 0, @strings;
The remaining list-reduction functions are all specialisations of this generic idea.
any
my $bool = any { BLOCK }, @list;
Similar to grep
in that it evaluates BLOCK
setting $_
to each element of @list
in turn. any
returns true if any element makes the BLOCK
return a true value. If BLOCK
never returns true or @list
was empty then it returns false.
Many cases of using grep
in a conditional can be written using any
instead, as it can short-circuit after the first true result.
if( any { .chars > 10 }, @strings ) {
# at least one string has more than 10 characters
}
all
my $bool = all { BLOCK }, @list;
Similar to /any, except that it requires all elements of the @list
to make the BLOCK
return true. If any element returns false, then it returns false. If the BLOCK
never returns false or the @list
was empty then it returns true.
none
notall
my $bool = none { BLOCK }, @list;
my $bool = notall { BLOCK }, @list;
Similar to /any and /all, but with the return sense inverted. none
returns true only if no value in the @list
causes the BLOCK
to return true, and notall
returns true only if not all of the values do.
first
my $val = first { BLOCK }, @list;
Similar to grep
in that it evaluates BLOCK
setting $_
to each element of @list
in turn. first
returns the first element where the result from BLOCK
is a true value. If BLOCK
never returns true or @list
was empty then Nil
is returned.
$foo = first { defined($_) }, @list; # first defined value in @list
$foo = first { $_ > $value }, @list; # first value in @list which
# is greater than $value
max
my $num = max @list;
Returns the entry in the list with the highest numerical value. If the list is empty then Nil
is returned.
$foo = max 1..10; # 10
$foo = max 3,9,12; # 12
$foo = max @bar, @baz; # whatever
maxstr
my $str = maxstr @list;
Similar to /max, but treats all the entries in the list as strings and returns the highest string as defined by the gt
operator. If the list is empty then Nil
is returned.
$foo = maxstr 'A'..'Z'; # 'Z'
$foo = maxstr "hello","world"; # "world"
$foo = maxstr @bar, @baz; # whatever
min
my $num = min @list;
Similar to /max but returns the entry in the list with the lowest numerical value. If the list is empty then Nil
is returned.
$foo = min 1..10; # 1
$foo = min 3,9,12; # 3
$foo = min @bar, @baz; # whatever
minstr
my $str = minstr @list;
Similar to /min, but treats all the entries in the list as strings and returns the lowest string as defined by the lt
operator. If the list is empty then Nil
is returned.
$foo = minstr 'A'..'Z'; # 'A'
$foo = minstr "hello","world"; # "hello"
$foo = minstr @bar, @baz; # whatever
product
my $num = product @list;
Returns the numerical product of all the elements in @list
. If @list
is empty then 1
is returned.
$foo = product 1..10; # 3628800
$foo = product 3,9,12; # 324
sum
my $num_or_nil = sum @list;
Returns the numerical sum of all the elements in @list
. For backwards compatibility, if @list
is empty then Nil
is returned.
$foo = sum 1..10; # 55
$foo = sum 3,9,12; # 24
$foo = sum @bar, @baz; # whatever
sum0
my $num = sum0 @list;
Similar to /sum, except this returns 0 when given an empty list, rather than Nil
.
KEY/VALUE PAIR LIST FUNCTIONS
The following set of functions, all inspired by List::Pairwise, consume an even-sized list of pairs. The pairs may be key/value associations from a hash, or just a list of values. The functions will all preserve the original ordering of the pairs, and will not be confused by multiple pairs having the same "key" value - nor even do they require that the first of each pair be a plain string.
NOTE: At the time of writing, the following pair*
functions that take a block do not modify the value of $_
within the block, and instead operate using the $a
and $b
globals instead. This has turned out to be a poor design, as it precludes the ability to provide a pairsort
function. Better would be to pass pair-like objects as 2-element array references in $_
, in a style similar to the return value of the pairs
function. At some future version this behaviour may be added.
Until then, users are alerted NOT to rely on the value of $_
remaining unmodified between the outside and the inside of the control block. In particular, the following example is UNSAFE:
my @kvlist = ...
foreach (qw( some keys here )) {
my @items = pairgrep { $a eq $_ } @kvlist;
...
}
Instead, write this using a lexical variable:
foreach my $key (qw( some keys here )) {
my @items = pairgrep { $a eq $key } @kvlist;
...
}
pairs
my @pairs = pairs @kvlist;
A convenient shortcut to operating on even-sized lists of pairs, this function returns a list of ARRAY
references, each containing two items from the given list. It is a more efficient version of
@pairs = pairmap { [ $a, $b ] }, @kvlist;
It is most convenient to use in a foreach
loop, for example:
foreach my $pair ( pairs @kvlist ) {
my ( $key, $value ) = @$pair;
...
}
The following code is equivalent:
foreach my $pair ( pairs @kvlist ) {
my $key = $pair.key;
my $value = $pair.value;
...
}
unpairs
my @kvlist = unpairs @pairs;
The inverse function to pairs
; this function takes a list of ARRAY
references containing two elements each, and returns a flattened list of the two values from each of the pairs, in order. This is notionally equivalent to
my @kvlist = map { @{$_}[0,1] } @pairs;
except that it is implemented more efficiently internally. Specifically, for any input item it will extract exactly two values for the output list; using Nil
if the input array references are short.
Between pairs
and unpairs
, a higher-order list function can be used to operate on the pairs as single scalars; such as the following near-equivalents of the other pair*
higher-order functions:
@kvlist = unpairs grep { FUNC }, pairs @kvlist;
# Like pairgrep, but takes $_ instead of $a and $b
@kvlist = unpairs map { FUNC }, pairs @kvlist;
# Like pairmap, but takes $_ instead of $a and $b
Finally, this technique can be used to implement a sort on a keyvalue pair list; e.g.:
@kvlist = unpairs sort -> $a, $b { $a.key cmp $b.key }, pairs @kvlist;
pairkeys
my @keys = pairkeys @kvlist;
A convenient shortcut to operating on even-sized lists of pairs, this function returns a list of the the first values of each of the pairs in the given list. It is a more efficient version of
@keys = pairmap -> $a, $b { $a }, @kvlist;
pairvalues
my @values = pairvalues @kvlist;
A convenient shortcut to operating on even-sized lists of pairs, this function returns a list of the the second values of each of the pairs in the given list. It is a more efficient version of
@values = pairmap -> $a, $b { $b }, @kvlist;
pairgrep
my @kvlist = pairgrep -> $a, $b { BLOCK }, @kvlist;
my $count = pairgrep -> $a, $b { BLOCK }, @kvlist;
Similar to perl's grep
keyword, but interprets the given list as an even-sized list of pairs. It invokes the BLOCK
multiple times, with $a
and $b
set to successive pairs of values from the @kvlist
.
Returns an even-sized list of those pairs for which the BLOCK
returned true.
@subset = pairgrep -> $a, $b { $a =~ m/^[[:upper:]]+$/ }, @kvlist;
As with grep
aliasing $_
to list elements, pairgrep
aliases $a
and $b
to elements of the given list.
pairfirst
my ( $key, $val ) = pairfirst -> $a, $b { BLOCK }, @kvlist;
my $found = pairfirst -> $a, $b { BLOCK }, @kvlist;
Similar to the /first function, but interprets the given list as an even-sized list of pairs. It invokes the BLOCK
multiple times, with $a
and $b
set to successive pairs of values from the @kvlist
.
Returns the first pair of values from the list for which the BLOCK
returned true, or an empty list of no such pair was found.
( $key, $value ) = pairfirst -> $a, $b { $a =~ m/^[[:upper:]]+$/ }, @kvlist;
As with grep
aliasing $_
to list elements, pairfirst
aliases $a
and $b
to elements of the given list.
pairmap
my @list = pairmap -> $a, $b { BLOCK }, @kvlist;
my $count = pairmap -> $a, $b { BLOCK }, @kvlist;
Similar to perl's map
keyword, but interprets the given list as an even-sized list of pairs. It invokes the BLOCK
multiple times, with $a
and $b
set to successive pairs of values from the @kvlist
.
Returns all the values returned by the BLOCK
.
@result = pairmap -> $a, $b { "The key $a has value $b" }, @kvlist
As with map
aliasing $_
to list elements, pairmap
aliases $a
and $b
to elements of the given list.
OTHER FUNCTIONS
shuffle
my @values = shuffle @values;
Returns the values of the input in a random order
@cards = shuffle 0..51; # 0..51 in a random order
uniq
my @subset = uniq @values;
Filters a list of values to remove subsequent duplicates, as judged by a DWIM-ish string equality. Preserves the order of unique elements, and retains the first value of any duplicate set.
Type objects are treated by this function as distinct from the empty string, and no warning will be produced. It is left as-is in the returned list. Subsequent identical type objects are still considered identical to the first, and will be removed.
uniqnum
my @subset = uniqnum @values;
Filters a list of values to remove subsequent duplicates, as judged by a numerical equality test. Preserves the order of unique elements, and retains the first value of any duplicate set.
Note that type objects are treated much as other numerical operations treat it; it compares equal to zero but additionally produces a warning. In addition, a type object in the returned list is coerced into a numerical zero, so that the entire list of values returned by uniqnum
are well-behaved as numbers.
Note also that multiple IEEE NaN
values are treated as duplicates of each other, regardless of any differences in their payloads.
uniqstr
my @subset = uniqstr @values;
Filters a list of values to remove subsequent duplicates, as judged by a string equality test. Preserves the order of unique elements, and retains the first value of any duplicate set.
Note that type objects are treated much as other string operations treat it; it compares equal to the empty string but additionally produces a warning. In addition, a type object in the returned list is coerced into an empty string, so that the entire list of values returned by uniqstr
are well-behaved as Str.
SEE ALSO
AUTHOR
Elizabeth Mattijsen [email protected]
Source can be located at: https://github.com/lizmat/List-Util . Comments and Pull Requests are welcome.
COPYRIGHT AND LICENSE
Copyright 2018, 2019, 2020, 2021 Elizabeth Mattijsen
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.
Re-imagined from the Perl version as part of the CPAN Butterfly Plan. Perl version originally developed by Graham Barr, subsequently maintained by Paul Evans.