Class: Transformer

Inherits:
Object show all
Defined in:
lib/coroutines/base.rb,
lib/coroutines/operators.rb

Overview

A class implementing transformer coroutines

A Transformer can be created by the following methods:

  • Object#trans_for

  • Transformer.new

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.

Defined Under Namespace

Classes: FirstYielder, Lazy, SecondYielder

Instance Method Summary collapse

Constructor Details

#initialize(&block) ⇒ Transformer

:call-seq:

Transformer.new { |yielder| ... } -> trans

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]


164
165
166
# File 'lib/coroutines/base.rb', line 164

def initialize(&block)
	@self = block
end

Instance Method Details

#in_connect(source) ⇒ Object Also known as: <=

:call-seq:

trans.in_connect(other_trans)  -> new_trans
trans.in_connect(enum)         -> lazy_enumerator

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.



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# 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

#inspectObject



168
169
170
# File 'lib/coroutines/base.rb', line 168

def inspect
	"#<Transformer: 0x#{object_id.to_s(16)}>"
end

#lazyObject

:call-seq:

trans.lazy -> lazy_trans

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"]


534
535
536
# File 'lib/coroutines/base.rb', line 534

def lazy
	Lazy.new self
end

#out_connect(sink) ⇒ Object Also known as: >=

:call-seq:

trans.out_connect(other_trans)  -> new_trans
trans.out_connect(sink)         -> consum

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.



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# 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

#to_transObject

:call-seq:

transformer.to_trans  -> transformer

Returns self.



176
177
178
# File 'lib/coroutines/base.rb', line 176

def to_trans
	self
end