mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-enum-lazy: add comprehensive call-seq documentation for lazy enumeration
Added complete call-seq documentation for all lazy enumeration methods in mrblib/lazy.rb (16 methods): ## Enumerable Extension Methods: - lazy: creates Enumerator::Lazy for deferred evaluation, enables efficient processing of infinite sequences and large datasets with comprehensive pythagorean triples example demonstrating real-world usage ## Enumerator::Lazy Class Methods: - new: constructor for creating lazy enumerators with custom yielding logic, provides foundation for building custom lazy operations - to_enum/enum_for: creates lazy enumerator from method calls, maintains lazy evaluation chain for custom enumerable methods ## Enumerator::Lazy Instance Methods: - map/collect: lazy transformation of elements with deferred execution - select/find_all: lazy filtering with conditional element inclusion - reject: lazy filtering with conditional element exclusion - grep: lazy pattern matching using case equality operator - grep_v: lazy inverse pattern matching for exclusion filtering - drop: lazy skipping of first n elements without immediate evaluation - drop_while: lazy conditional skipping until predicate fails - take: lazy limiting to first n elements with automatic termination - take_while: lazy conditional taking until predicate fails - flat_map/collect_concat: lazy flattening and mapping in single operation - zip: lazy combining of multiple enumerables into tuples - uniq: lazy uniqueness filtering with optional transformation block - force: immediate evaluation alias for to_a, converts lazy chain to array Co-authored-by: Atlassian Rovo Dev
This commit is contained in:
@@ -1,21 +1,34 @@
|
||||
module Enumerable
|
||||
|
||||
# = Enumerable#lazy implementation
|
||||
#
|
||||
# Enumerable#lazy returns an instance of Enumerator::Lazy.
|
||||
# You can use it just like as normal Enumerable object,
|
||||
# except these methods act as 'lazy':
|
||||
# call-seq:
|
||||
# enum.lazy -> lazy_enumerator
|
||||
#
|
||||
# Returns an Enumerator::Lazy, which redefines most Enumerable
|
||||
# methods to postpone enumeration and enumerate values only on an
|
||||
# as-needed basis.
|
||||
#
|
||||
# === Example
|
||||
#
|
||||
# The following program finds pythagorean triples:
|
||||
#
|
||||
# def pythagorean_triples
|
||||
# (1..Float::INFINITY).lazy.flat_map {|z|
|
||||
# (1..z).flat_map {|x|
|
||||
# (x..z).select {|y|
|
||||
# x*x + y*y == z*z
|
||||
# }.map {|y|
|
||||
# [x, y, z]
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# end
|
||||
# # show first ten pythagorean triples
|
||||
# p pythagorean_triples.take(10).force # take is lazy, so force is needed
|
||||
# p pythagorean_triples.first(10) # first is eager
|
||||
# # show pythagorean triples less than 100
|
||||
# p pythagorean_triples.take_while { |*, z| z < 100 }.force
|
||||
#
|
||||
# - map collect
|
||||
# - select find_all
|
||||
# - reject
|
||||
# - grep
|
||||
# - grep_v
|
||||
# - drop
|
||||
# - drop_while
|
||||
# - take_while
|
||||
# - flat_map collect_concat
|
||||
# - zip
|
||||
def lazy
|
||||
Enumerator::Lazy.new(self)
|
||||
end
|
||||
@@ -28,6 +41,21 @@ class Enumerator
|
||||
# Inspired by https://github.com/antimon2/enumerable_lz
|
||||
# http://jp.rubyist.net/magazine/?0034-Enumerable_lz (ja)
|
||||
class Lazy < Enumerator
|
||||
#
|
||||
# call-seq:
|
||||
# Lazy.new(obj, &block)
|
||||
#
|
||||
# Creates a new Lazy enumerator. When the enumerator is actually enumerated
|
||||
# (e.g. by calling #force), obj will be enumerated and each value passed
|
||||
# to the given block. The block can yield values back by calling yielder.yield.
|
||||
# For example, to create a method that acts like Array#select:
|
||||
#
|
||||
# def select
|
||||
# Lazy.new(self) do |yielder, value|
|
||||
# yielder.yield(value) if yield(value)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
def initialize(obj, &block)
|
||||
super(){|yielder|
|
||||
begin
|
||||
@@ -43,6 +71,25 @@ class Enumerator
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.to_enum(method = :each, *args) -> lazy_enum
|
||||
# lazy.to_enum(method = :each, *args) {|*args| ... } -> lazy_enum
|
||||
# lazy.enum_for(method = :each, *args) -> lazy_enum
|
||||
# lazy.enum_for(method = :each, *args) {|*args| ... } -> lazy_enum
|
||||
#
|
||||
# Similar to Object#to_enum, except it returns a lazy enumerator.
|
||||
# This makes it easy to define Enumerable methods that will
|
||||
# naturally remain lazy if called on a lazy enumerator.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# module Enumerable
|
||||
# def filter_map(&block)
|
||||
# map(&block).compact
|
||||
# end
|
||||
# end
|
||||
#
|
||||
def to_enum(meth=:each, *args, &block)
|
||||
unless self.respond_to?(meth)
|
||||
raise ArgumentError, "undefined method #{meth}"
|
||||
@@ -58,6 +105,18 @@ class Enumerator
|
||||
end
|
||||
alias enum_for to_enum
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.map {|obj| block } -> lazy_enumerator
|
||||
# lazy.collect {|obj| block } -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#map, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.map {|i| i**2 }
|
||||
# #=> #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>:map>
|
||||
# (1..Float::INFINITY).lazy.map {|i| i**2 }.first(3)
|
||||
# #=> [1, 4, 9]
|
||||
#
|
||||
def map(&block)
|
||||
Lazy.new(self){|yielder, val|
|
||||
yielder << block.call(val)
|
||||
@@ -65,6 +124,16 @@ class Enumerator
|
||||
end
|
||||
alias collect map
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.select {|obj| block } -> lazy_enumerator
|
||||
# lazy.find_all {|obj| block } -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#select, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.select {|i| i.even? }.first(3)
|
||||
# #=> [2, 4, 6]
|
||||
#
|
||||
def select(&block)
|
||||
Lazy.new(self){|yielder, val|
|
||||
if block.call(val)
|
||||
@@ -74,6 +143,15 @@ class Enumerator
|
||||
end
|
||||
alias find_all select
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.reject {|obj| block } -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#reject, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.reject {|i| i.even? }.first(3)
|
||||
# #=> [1, 3, 5]
|
||||
#
|
||||
def reject(&block)
|
||||
Lazy.new(self){|yielder, val|
|
||||
unless block.call(val)
|
||||
@@ -82,6 +160,15 @@ class Enumerator
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.grep(pattern) -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#grep, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.grep(1..10).force
|
||||
# #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
#
|
||||
def grep(pattern)
|
||||
Lazy.new(self){|yielder, val|
|
||||
if pattern === val
|
||||
@@ -90,6 +177,15 @@ class Enumerator
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.grep_v(pattern) -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#grep_v, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.grep_v(2..4).first(3)
|
||||
# #=> [1, 5, 6]
|
||||
#
|
||||
def grep_v(pattern)
|
||||
Lazy.new(self){|yielder, val|
|
||||
unless pattern === val
|
||||
@@ -98,6 +194,15 @@ class Enumerator
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.drop(n) -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#drop, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.drop(3).first(3)
|
||||
# #=> [4, 5, 6]
|
||||
#
|
||||
def drop(n)
|
||||
dropped = 0
|
||||
Lazy.new(self){|yielder, val|
|
||||
@@ -109,6 +214,15 @@ class Enumerator
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.drop_while {|obj| block } -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#drop_while, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.drop_while {|i| i < 4 }.first(3)
|
||||
# #=> [4, 5, 6]
|
||||
#
|
||||
def drop_while(&block)
|
||||
dropping = true
|
||||
Lazy.new(self){|yielder, val|
|
||||
@@ -123,6 +237,15 @@ class Enumerator
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.take(n) -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#take, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.take(3).force
|
||||
# #=> [1, 2, 3]
|
||||
#
|
||||
def take(n)
|
||||
if n == 0
|
||||
return Lazy.new(self){raise StopIteration}
|
||||
@@ -137,6 +260,15 @@ class Enumerator
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.take_while {|obj| block } -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#take_while, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.take_while {|i| i < 4 }.force
|
||||
# #=> [1, 2, 3]
|
||||
#
|
||||
def take_while(&block)
|
||||
Lazy.new(self){|yielder, val|
|
||||
if block.call(val)
|
||||
@@ -147,6 +279,16 @@ class Enumerator
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.flat_map {|obj| block } -> lazy_enumerator
|
||||
# lazy.collect_concat {|obj| block } -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#flat_map, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# ["foo", "bar"].lazy.flat_map {|i| i.each_char.lazy}.force
|
||||
# #=> ["f", "o", "o", "b", "a", "r"]
|
||||
#
|
||||
def flat_map(&block)
|
||||
Lazy.new(self){|yielder, val|
|
||||
ary = block.call(val)
|
||||
@@ -158,6 +300,17 @@ class Enumerator
|
||||
end
|
||||
alias collect_concat flat_map
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.zip(arg, ...) -> lazy_enumerator
|
||||
# lazy.zip(arg, ...) {|arr| block } -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#zip, but chains operation to be lazy-evaluated.
|
||||
# However, if a block is given to zip, values are enumerated immediately.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.zip(('a'..'z').cycle).first(3)
|
||||
# #=> [[1, "a"], [2, "b"], [3, "c"]]
|
||||
#
|
||||
def zip(*args, &block)
|
||||
enums = [self] + args
|
||||
Lazy.new(self){|yielder, val|
|
||||
@@ -170,6 +323,16 @@ class Enumerator
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.uniq -> lazy_enumerator
|
||||
# lazy.uniq {|item| block } -> lazy_enumerator
|
||||
#
|
||||
# Like Enumerable#uniq, but chains operation to be lazy-evaluated.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.map {|i| i % 3}.uniq.first(3)
|
||||
# #=> [1, 2, 0]
|
||||
#
|
||||
def uniq(&block)
|
||||
hash = {}
|
||||
Lazy.new(self){|yielder, val|
|
||||
@@ -185,6 +348,16 @@ class Enumerator
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# call-seq:
|
||||
# lazy.force -> array
|
||||
#
|
||||
# Forces lazy evaluation and returns an array containing the values
|
||||
# enumerated by the lazy enumerator. This is an alias for to_a.
|
||||
#
|
||||
# (1..Float::INFINITY).lazy.take(3).force
|
||||
# #=> [1, 2, 3]
|
||||
#
|
||||
alias force to_a
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user