NAME

Sidef::Object::Enumerator

DESCRIPTION

This class implements an enumerator object, allowing for functional-style operations such as mapping, filtering, and accessing elements within a collection. The enumerator is designed with lazy evaluation in mind, making it suitable for working with potentially infinite sequences.

An enumerator is created with a block that receives a callback function. The block calls this callback for each element it wants to yield. This enables the creation of custom iterators and generators.

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]

METHODS

new

Enumerator(block)
self. new

Creates a new enumerator instance with the given block. The block receives a callback function (commonly named yield or f or cb) that should be called for each element the enumerator produces.

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

Aliases: call

count

self.count(block)

Counts and returns the number of elements in the enumerator collection that satisfy the given block condition.

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

Note: This method consumes the entire enumerator.

Aliases: count_by

each

self.each(block)

Iterates over all elements in the enumerator, passing each element to the given block. Returns the enumerator itself.

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

Warning: Do not use on infinite enumerators as it will loop forever.

first

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

Returns the first n elements of the enumerator as an array. If a block is provided, it retrieves the first n elements that satisfy the block condition.

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

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

grep

self.grep(block)

Filters the elements in the enumerator collection based on the given block condition. Returns a new array containing only the elements for which the block returns a true value.

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

Note: This method consumes the entire enumerator.

Aliases: select

len

self.len

Returns the total number of elements in the enumerator by consuming all elements and counting them.

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

Warning: Do not use on infinite enumerators as it will loop forever.

Aliases: size, length

map

self.map(block)

Applies the given block to each element of the enumerator and returns a new array with the transformed elements.

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

Note: This method consumes the entire enumerator.

Aliases: collect

nth

e.nth(n)
e.nth(n, block)

Retrieves the n-th element from the enumerator collection (1-indexed). If an optional block is provided, it counts only the elements for which the block returns a true value, returning the n-th matching element.

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

say e.nth(10)                   # 10
say e. nth(10, { .is_prime })    # 29 (the 10th prime number)

to_a

self.to_a

Converts the enumerator into an array by consuming all elements and collecting them. Returns the resulting array.

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

Warning: Do not use on infinite enumerators as it will loop forever.

while

self.while(block)

Yields elements to the given block and collects them into an array until the block returns false. Stops iterating immediately when the block returns a false value.

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

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

This method is particularly useful for working with infinite enumerators when you want to collect elements up to a certain condition.

EXAMPLES

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)

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, {_ > 50})   # First 20 primes greater than 50

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(" ") }

SEE ALSO

Sidef::Types::Array::Array, Sidef::Types::Range::RangeNumber