=encoding utf8

=head1 NAME

Sidef::Object::Enumerator - Lazy iterator object for generating and processing custom sequences.

=head1 DESCRIPTION

This class implements a general-purpose enumerator: an object built around a single "generator" block that produces values by calling back into a callback it's given, however it likes (a loop, recursion, an infinite C<loop { ... }>, and so on). This makes it easy to build custom lazy sequences -- including infinite ones -- without writing a dedicated iterator class.

An C<Enumerator> is created with a block that receives one argument, conventionally named C<yield>, C<f>, or C<cb>: a callback the block should call, once per produced value, for as long as it wants to keep producing values.

Two important behavioral points to keep in mind:

=over 4

=item * B<Early exit.> L</first>, L</nth>, and L</while> can stop consuming the generator block as soon as they have what they need -- internally, by jumping straight out of the (possibly still-running, possibly infinite) generator block the moment enough values have been produced. This is what makes it safe to call, say, C<.first(10)> on a generator that loops forever. Every I<other> method (L</each>, L</to_a>, L</map>, L</grep>, L</count>, L</length>) has no such escape hatch and will run the generator block to completion -- looping forever if it never stops on its own.

=item * B<Multi-value yields.> The callback can technically be invoked with more than one value at a time (e.g. C<yield(a, b)>). L</map> and L</grep> handle this correctly, applying the block to each yielded value individually. L</first>, L</nth>, L</count>, and L</length>, however, treat each I<callback invocation> as a single unit: they don't count or split up multiple values yielded together in one call. In practice, nearly every generator yields one value at a time, so this rarely matters.

=back

=head1 SYNOPSIS

    # Create an enumerator that yields prime numbers
    var primes = Enumerator({|yield|
        for n in (2..Inf) {
            yield(n) if n.is_prime
        }
    })

    # Get the first 10 primes
    say(primes.first(10))    #=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

    # Create a Fibonacci sequence enumerator
    var fib = Enumerator({|yield|
        var (a, b) = (0, 1)
        loop {
            yield(a)
            (a, b) = (b, a + b)
        }
    })

    say(fib.first(10))       #=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

=head1 INHERITS

Inherits methods from L<Sidef::Object::Object>.

=head1 CONSTRUCTION

=head2 new

    Enumerator(block)
    Enumerator.new(block)

Creates a new enumerator from C<block>. C<block> receives a single callback argument (commonly named C<yield>, C<f>, or C<cb>) that it should call once per value it wants the enumerator to produce.

    var squares = Enumerator({|yield|
        for n in (1..Inf) {
            yield(n * n)
        }
    })

Aliases: I<call>

=cut

=head1 EARLY-EXIT CONSUMPTION

These methods can stop consuming the generator block as soon as they have enough values, so they're safe to use even on an enumerator that produces values forever.

=head2 first

    self.first(n)
    self.first(n, block)

Returns an C<Array> of the first C<n> values produced by the enumerator. If C<block> is given, only values for which C<block> returns a truthy value are counted and collected. If C<n> is less than or equal to C<1>, only the very first (matching) value found is collected before stopping.

    var e = Enumerator({|f| (1..Inf).each {|i| f(i) } })

    say(e.first(5))                   #=> [1, 2, 3, 4, 5]
    say(e.first(5, {|n| n.is_prime })) #=> [2, 3, 5, 7, 11]

=cut

=head2 nth

    self.nth(n)
    self.nth(n, block)

Returns the C<n>-th (1-indexed) value produced by the enumerator, or C<nil> if the enumerator stops before reaching it. If C<block> is given, only values for which C<block> returns a truthy value are counted, so this returns the C<n>-th I<matching> value. If C<n> is less than or equal to C<1>, this returns the first (matching) value found.

    var primes = Enumerator({|f| (2..Inf).each {|i| f(i) if i.is_prime } })

    say(primes.nth(10))    #=> 29   # the 10th prime
    say(primes.nth(100))   #=> 541  # the 100th prime

=cut

=head2 while

    self.while(block)

Collects values produced by the enumerator into an C<Array> for as long as C<block> returns a truthy value for each one, and stops -- without including the value that failed -- as soon as C<block> returns a falsy value.

    var palindromes = Enumerator({|f|
        var n = 1
        loop {
            f(n)
            n = n.next_palindrome!
        }
    })

    # Get all palindromes less than 100
    say(palindromes.while {|n| n < 100 })
    #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99]

=cut

=head1 FULL CONSUMPTION

These methods always run the generator block to completion, with no early exit -- they will never return if used on a genuinely infinite enumerator.

=head2 each

    self.each(block)

Calls C<block> once for every value the enumerator produces (C<block> I<is> the callback the generator block invokes directly), and returns C<self>.

    var e = Enumerator({|f| (1..5).each {|i| f(i) } })
    e.each {|n| say(n) }    # prints 1, 2, 3, 4, 5

=cut

=head2 to_a

    self.to_a

Collects every value the enumerator produces into an C<Array>.

    var e = Enumerator({|f| (1..5).each {|i| f(i) } })
    say(e.to_a)    #=> [1, 2, 3, 4, 5]

=cut

=head2 map

    self.map(block)

Applies C<block> to every value the enumerator produces and returns a new C<Array> of the results.

    var e = Enumerator({|f| (1..5).each {|i| f(i) } })
    say(e.map {|n| n * 2 })    #=> [2, 4, 6, 8, 10]

Aliases: I<collect>

=cut

=head2 grep

    self.grep(block)

Returns a new C<Array> containing only the values produced by the enumerator for which C<block> returns a truthy value.

    var e = Enumerator({|f| (1..20).each {|i| f(i) } })
    say(e.grep {|n| n.is_even })    #=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

Aliases: I<select>

=cut

=head2 count

    self.count(block)

Returns the number of values produced by the enumerator for which C<block> returns a truthy value.

    var e = Enumerator({|f| (1..100).each {|i| f(i) } })
    say(e.count {|n| n.is_prime })    #=> 25

Aliases: I<count_by>

=cut

=head2 length

    self.length

Returns the total number of values the enumerator produces.

    var e = Enumerator({|f| (1..100).each {|i| f(i) if i.is_prime } })
    say(e.length)    #=> 25

Aliases: I<len>, I<size>

=cut

=head1 EXAMPLES

=head2 Ludic Numbers

    func ludics_upto(nmax = 100000) {
        Enumerator({|collect|
            collect(1)
            var arr = @(2..nmax)
            while (arr) {
                collect(var n = arr[0])
                arr.range.by(n).each {|i| arr[i] = nil }
                arr.compact!
            }
        })
    }

    say(ludics_upto(1000).first(25))

=head2 Infinite Prime Generator

    var primes = Enumerator({|yield|
        for n in (2..Inf) {
            yield(n) if n.is_prime
        }
    })

    say(primes.first(10))              # first 10 primes
    say(primes.nth(100))               # the 100th prime
    say(primes.first(20, {|n| n > 50 })) # first 20 primes greater than 50

=head2 Word Wrapping

    func wordwrap(words, maxwidth) {
        Enumerator({|y|
            var cols = 0
            words.slice_before {|w|
                cols += 1 if (cols != 0)
                cols += w.len
                if (maxwidth < cols) {
                    cols = w.len
                    true
                } else {
                    false
                }
            }.each {|ws| y(ws) }
        })
    }

    var text = "The quick brown fox jumps over the lazy dog"
    wordwrap(text.words, 15).each {|line| say(line.join(" ")) }

=head1 SEE ALSO

L<Sidef::Types::Array::Array>, L<Sidef::Types::Range::RangeNumber>

=cut
