NAME
List::Lazy - Generate lists lazily
VERSION
version 0.4.0
SYNOPSIS
use List::Lazy qw/ lazy_range /;
my $range = lazy_range( 1, undef )->grep(sub{ $_ % 2})->map( sub { '!' x $_ } );
say $_ for $range->next(3); # prints ! !!! !!!!!
DESCRIPTION
List::Lazy creates lists that lazily evaluate their next values on-demand.
EXPORTED FUNCTIONS
Lazy::List doesn't export any function by default, but will export the three following functions on request.
lazy_list
my $list  = lazy_list $generator_sub, $state;
A convenience shortcut for the List::Lazy constructor. The $state will be available (and can be changed) by the generator subroutine. The generator subroutine is expected to return a list of one or more next items of the list. Returning an empty list means that the list has reached its end.
my $even_numbers = lazy_list { $_ += 2 } 0; # will return 2, 4, 6, ...
In additional of regular values, the generator can also return lazy lists, which will be seamlessly expanded.
my $list = lazy_range( 1, undef )->map(sub { lazy_range( 1, $_ ) });
# will return 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, ...
lazy_range
my $range = lazy_range $min, $max, $iterator;
Creates a list iterating over a range of values. $min and $max are required, but $max can be undef (meaning no upper limit). The $iterator is optional and defaults to the value 1. The $iterator can be a number, which will be the step at which the numbers are increased, or a coderef that will be passed the previous value as $_, and is expected to return the next value.
my $palinumbers = lazy_range 99, undef, sub { do { $_++ } until $_ eq reverse $_; $_ };
say join ' ', $palinumbers->next(3); # 99 101 111
lazy_fixed_list
my $list = lazy_fixed_list @some_array;
Creates a lazy list that will returns the values of the given array.
CLASS
new
my $list = List::Lazy->new(
    state => 1,
    generator => sub {
        $_++;
    },
);
Creates a lazy list.
arguments
- state
 - 
The state will be passed to the generator as
$_. If it is modified by the generator, its new value will be saved for the next invocation. - generator
 - 
A coderef that generates one or more next items for the list. If it returns an empty list, the stream will be considered to be exhausted.
 
is_done
Returns true is the list is exhausted.
next($num)
Returns the next $num items of the list (or less if the list doesn't have that many items left). $num defaults to 1.
my $range = lazy_range 1, 100;
while( my $next = $range->next ) {
    ...
}
As a safety valve, if the number of entries to be returned exceeds <$Lazy::List::MAX_NEXT> (which defaults to 10_000), the call will die.
reduce
my $value = $list->reduce( $reducing_sub, $initial_value );
Iterates through the list and reduces its values via the $reducing_sub, which will be passed the cumulative value and the next item via $a and $b. If $initial_value is not given, it defaults to the first element of the list.
my $sum = lazy_range( 1, 100 )->reduce( sub { $a + $b } );
batch
my $new_list = $list->batch($n);
Creates a new list where the items of the original list are batched in groups of $n (or less for the last batch).
my $list = lazy_fixed_list( 1..100 )->batch(3);
my $x = $list->next;           # $x == [ 1, 2, 3]
map
my $new_list = $list->map( $mapper_sub );
Creates a new list by applying the transformation given by $mapper_sub to the original list. The sub ill be passed the original next item via $_ and is expected to return its transformation, which can modify the item, explode it into many items, or suppress it,
Note that the new list do a deep clone of the original list's state, so reading from the new list won't affect the original list.
my $recount = ( lazy_range 1, 100 )->map( sub { 1..$_ } );
# will return 1 1 2 1 2 3 1 2 3 4 ...
grep
my $new_list = $list->grep( $filter_sub );
Creates a new list by applying the filtering given by $filter_sub to the original list. The sub will be passed the original next item via $_ and is expected to return a boolean indicating if the item should be kept or not.
Note that the new list do a deep clone of the original list's state, so reading from the new list won't affect the original list.
my $odd = ( lazy_range 1, 100 )->grep( sub { $_ % 2 } );
spy
my $new_list = $list->spy( $sub );
Creates a new list that will execute the spy $sub for every value it sees (with the value assigned to $_).
If $sub is not given, it'll carp the values.
until
my $new_list = $list->until( $condition );
Creates a new list that truncates the original list as soon as the condition is met.
my $to_ten = $list->until(sub{ $_ > 10 });
append
my $new_list = $list->append( @other_lists );
Creates a new list that will return first the elements of $list, and those of the @other_lists.
Note that the new list do a deep clone of the original lists's state, so reading from the new list won't affect the original lists.
my $range = lazy_range 1, 100;
my $twice = $range->append( $range );
Non-lazy lists can also be given to append, in which case they will be converted into lazy_fixed_lists under the hood.
my $list = lazy_range 1, 100;
$list = $list->append( 101, lazy_range(102,200), 201..210 );
# equivalent to
$list = $list->append( 
    fixed_lazy_list(101), 
    lazy_range(102,200), 
    fixed_lazy_list(201..210) 
);
prepend
my $new_list = $list->prepend( @other_lists );
Like append, but prepend the other lists to the current one.
Note that the new list do a deep clone of the original lists's state, so reading from the new list won't affect the original lists.
all
my @rest = $list->all;
Returns all the remaining values of the list. As for next, this method will die if the number of entries generated exceeds $Lazy::List::MAX_NEXT.
AUTHOR
Yanick Champoux <yanick@babyl.dyndns.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2025 by Yanick Champoux.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.