class Transformer
A class implementing transformer coroutines
A Transformer
can be created by the following methods:
Transformers are pieces of code that accept input values and produce output values (without returning from their execution context like in a regular method call). They are used by connecting either their input to an enumerable or their output to a sink (or both, using Sink#in_connect
or Enumerable#out_connect
). An enumerable is any object implementing an iterator method each; a sink is any object implementing the << operator (which is assumed to store or output the supplied values in some form).
The input of a transformer is connected to an enumerable enum
using trans.in_connect(enum), the result of which is a new Enumerator
instance. The transformer is started upon iterating over this Enumerator
. Whenever the transformer requests a new input value (see Object#trans_for
and Transformer#new for how to do this), iteration over enum
is resumed. The output values of the transformer (again, see Object#trans_for
and Transformer#new) are yielded by the enclosing Enumerator
. When enum
is exhausted, StopIteration is raised at the point where the transformer last requested an input value. It is expected that the transformer will then terminate without requesting any more values (though it may execute e.g. some cleanup actions).
The output of a transformer is connected to a sink using trans.out_connect(sink), the result of which is a new Consumer
instance. The transformer starts executing right away; when it requests its first input value, out_connect
returns. Input values supplied using << to the enclosing Consumer
are forwarded to the transformer by resuming execution at the point where it last requested an input value. Output values produced by the transformer are fed to sink#<<. After terminating, the result of the new Consumer
is the value returned by sink.close (see Consumer#result
and Consumer#close
).
Transformers can also be chained together by connecting the output of one the input the next using trans.out_connect(other_trans) or trans.in_connect(other_trans). See Transformer#out_connect
and Transformer#in_connect
for details.
Public Class Methods
Creates a new Transformer
coroutine defined by the given block.
The block is called with a “yielder” object as parameter. yielder
can be used to retrieve a value from the consumption context by calling its await method (as in Consumer.new
), and to yield a value by calling its yield method (as in Enumerator.new).
running_sum = Transformer.new do |y| result = 0 loop { result += y.await; y.yield result } end (1..3).out_connect(running_sum).out_connect([]) # => [1, 3, 6]
# File lib/coroutines/base.rb, line 164 def initialize(&block) @self = block end
Public Instance Methods
In the first form, creates a new Transformer
that has the input of trans
connected to the output of other_trans
.
In the second form, creates a new lazy Enumerator
by connecting the output of enum
to the input of trans
. See Transformer
for details.
# File lib/coroutines/base.rb, line 189 def in_connect(source) if not source.respond_to? :each return source.to_trans.transformer_chain self end source_enum = source.to_enum enum = Enumerator.new do |y| y.define_singleton_method :await do source_enum.next end @self.call(y) end.lazy description = "#<Enumerator::Lazy: #{inspect} <= #{source.inspect}>" enum.define_singleton_method :inspect do description end enum end
# File lib/coroutines/base.rb, line 168 def inspect "#<Transformer: 0x#{object_id.to_s(16)}>" end
Returns a “lazy enumeration like” transformer. More precisely, the object returned can in many situations be used as if it were an Enumerator
returned by trans.in_connect, since it implements work-alikes of many Enumerable
methods. Note however that the return types of those methods differ: where an Enumerator
method would return a new Enumerator
, the corresponding lazy transformer returns a new Transformer
; where an Enumerator
would return a single value, the lazy transformer returns a Consumer
.
Example:
running_sum = Transformer.new do |y| result = 0 loop { result += y.await; y.yield result } end sum_str = running_sum.lazy.map{|x| x.to_s} # => a Transformer (1..10).out_connect(sum_str).to_a # => ["1", "3", "6", "10", "15", "21", "28", "36", "45", "55"]
# File lib/coroutines/base.rb, line 534 def lazy Lazy.new self end
In the first form, creates a new Transformer
that has the output of trans
connected to the input of other_trans
.
In the second form, creates a new Consumer
by connecting the output of trans
to the input of sink
. See Transformer
for details.
# File lib/coroutines/base.rb, line 219 def out_connect(sink) if not sink.respond_to? :<< return transformer_chain sink.to_trans end consum = Consumer.new do |y| y.define_singleton_method :yield do |args| sink << args y end y.singleton_class.instance_eval { alias_method :<<, :yield } begin @self.call(y) rescue StopIteration end sink.close end description = "#<Consumer: #{inspect} >= #{sink.inspect}>" consum.define_singleton_method :inspect do description end consum end
Returns self.
# File lib/coroutines/base.rb, line 176 def to_trans self end
Protected Instance Methods
# File lib/coroutines/base.rb, line 610 def transformer_chain(other) first = @self second = other.instance_variable_get(:@self) trans = Transformer.new do |y| fib = Fiber.new { first.call FirstYielder.new } second.call(SecondYielder.new y, fib) end description = "#{inspect} >= #{other.inspect}" trans.define_singleton_method :inspect do description end trans end