stream 0.5 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/stream.rb CHANGED
@@ -1,4 +1,4 @@
1
- STREAM_VERSION = "0.5"
1
+ STREAM_VERSION = '0.5.4'.freeze
2
2
 
3
3
  ##
4
4
  # Module Stream defines an interface for an external Iterator which
@@ -14,15 +14,20 @@ module Stream
14
14
  class EndOfStreamException < StandardError; end
15
15
 
16
16
  # Returns false if the next #forward will return an element.
17
- def at_end?; raise NotImplementedError; end
17
+ def at_end?
18
+ raise NotImplementedError
19
+ end
18
20
 
19
21
  # Returns false if the next #backward will return an element.
20
- def at_beginning?; raise NotImplementedError; end
22
+ def at_beginning?
23
+ raise NotImplementedError
24
+ end
21
25
 
22
26
  # Move forward one position. Returns the _target_ of current_edge.
23
27
  # Raises Stream::EndOfStreamException if at_end? is true.
24
28
  def forward
25
29
  raise EndOfStreamException if at_end?
30
+
26
31
  basic_forward
27
32
  end
28
33
 
@@ -30,28 +35,41 @@ module Stream
30
35
  # Stream::EndOfStreamException if at_beginning? is true.
31
36
  def backward
32
37
  raise EndOfStreamException if at_beginning?
38
+
33
39
  basic_backward
34
40
  end
35
41
 
36
42
  # Position the stream before its first element, i.e. the next #forward
37
43
  # will return the first element.
38
44
  def set_to_begin
39
- until at_beginning?; basic_backward; end
45
+ basic_backward until at_beginning?
40
46
  end
41
47
 
42
48
  # Position the stream behind its last element, i.e. the next #backward
43
49
  # will return the last element.
44
50
  def set_to_end
45
- until at_end?; basic_forward; end
51
+ basic_forward until at_end?
46
52
  end
47
53
 
48
54
  protected
49
55
 
50
- def basic_forward; raise NotImplementedError; end
51
- def basic_backward; raise NotImplementedError; end
56
+ def basic_forward
57
+ raise NotImplementedError
58
+ end
59
+
60
+ def basic_backward
61
+ raise NotImplementedError
62
+ end
52
63
 
53
- def basic_current; backward; forward; end
54
- def basic_peek; forward; backward; end
64
+ def basic_current
65
+ backward
66
+ forward
67
+ end
68
+
69
+ def basic_peek
70
+ forward
71
+ backward
72
+ end
55
73
 
56
74
  public
57
75
 
@@ -62,140 +80,212 @@ module Stream
62
80
  # current position. #detect, which is inherited from Enumerable uses
63
81
  # #each, which implicitly calls #set_to_begin.
64
82
  def move_forward_until
65
- until at_end?
66
- element = basic_forward
67
- return element if yield(element)
68
- end
69
- nil
83
+ until at_end?
84
+ element = basic_forward
85
+ return element if yield(element)
86
+ end
87
+ nil
70
88
  end
71
89
 
72
90
  # Move backward until the boolean block is not false and returns the element
73
91
  # found. Returns nil if no object matches.
74
92
  def move_backward_until
75
- until at_beginning?
76
- element = basic_backward
77
- return element if yield(element)
78
- end
79
- nil
80
- end
81
-
82
- # Returns the element returned by the last call of #forward. If at_beginning? is
83
- # true self is returned.
84
- def current; at_beginning? ? self : basic_current; end
93
+ until at_beginning?
94
+ element = basic_backward
95
+ return element if yield(element)
96
+ end
97
+ nil
98
+ end
99
+
100
+ # Returns the element returned by the last call of #forward. If at_beginning?
101
+ # is true self is returned.
102
+ def current
103
+ at_beginning? ? self : basic_current
104
+ end
85
105
 
86
106
  # Returns the element returned by the last call of #backward. If at_end? is
87
107
  # true self is returned.
88
- def peek; at_end? ? self : basic_peek; end
108
+ def peek
109
+ at_end? ? self : basic_peek
110
+ end
89
111
 
90
112
  # Returns the array [#current,#peek].
91
- def current_edge; [current,peek]; end
113
+ def current_edge
114
+ [current, peek]
115
+ end
92
116
 
93
117
  # Returns the first element of the stream. This is accomplished by calling
94
118
  # set_to_begin and #forward, which means a state change.
95
- def first; set_to_begin; forward; end
119
+ def first
120
+ set_to_begin
121
+ forward
122
+ end
96
123
 
97
124
  # Returns the last element of the stream. This is accomplished by calling
98
125
  # set_to_begin and #backward, which means a state change.
99
- def last; set_to_end; backward; end
126
+ def last
127
+ set_to_end
128
+ backward
129
+ end
100
130
 
101
131
  # Returns true if the stream is empty which is equivalent to at_end? and
102
132
  # at_beginning? both being true.
103
- def empty?; at_end? and at_beginning?; end
133
+ def empty?
134
+ at_end? and at_beginning?
135
+ end
104
136
 
105
137
  # Implements the standard iterator used by module Enumerable, by calling
106
138
  # set_to_begin and basic_forward until at_end? is true.
107
139
  def each
108
140
  set_to_begin
109
- until at_end?
110
- yield basic_forward
111
- end
141
+ yield basic_forward until at_end?
142
+ end
143
+
144
+ # create_stream is used for each Enumerable to create a stream for it. A
145
+ # Stream as an Enumerable returns itself.
146
+ def create_stream
147
+ self
112
148
  end
113
-
114
- # create_stream is used for each Enumerable to create a stream for it. A Stream as
115
- # an Enumerable returns itself.
116
- def create_stream; self end
117
149
 
118
150
  # A Stream::WrappedStream should return the wrapped stream unwrapped. If the
119
151
  # stream is not a wrapper around another stream it simply returns itself.
120
- def unwrapped; self; end
152
+ def unwrapped
153
+ self
154
+ end
121
155
 
122
156
  # The abstract super class of all concrete Classes implementing the Stream
123
157
  # interface. Only used for including module Stream.
124
158
  class BasicStream
125
- include Stream
159
+ include Stream
126
160
  end
127
161
 
128
- # A Singleton class for an empty stream. EmptyStream.instance is the sole instance
129
- # which answers true for both at_end? and at_beginning?
162
+ # A Singleton class for an empty stream. EmptyStream.instance is the sole
163
+ # instance which answers true for both at_end? and at_beginning?
130
164
  class EmptyStream < BasicStream
131
- require 'singleton'
132
- include Singleton
165
+ require 'singleton'
166
+ include Singleton
167
+
168
+ def at_end?
169
+ true
170
+ end
171
+
172
+ def at_beginning?
173
+ true
174
+ end
133
175
 
134
- def at_end?; true; end
135
- def at_beginning?; true; end
176
+ def basic_forward
177
+ raise EndOfStreamException
178
+ end
179
+
180
+ def basic_backward
181
+ raise EndOfStreamException
182
+ end
136
183
  end
137
184
 
138
- # A CollectionStream can be used as an external iterator for each interger-indexed
139
- # collection. The state of the iterator is stored in instance variable @pos.
185
+ # A CollectionStream can be used as an external iterator for each
186
+ # interger-indexed collection. The state of the iterator is stored in instance
187
+ # variable @pos.
140
188
  #
141
- # A CollectionStream for an array is created by the method Array#create_stream.
189
+ # A CollectionStream for an array is created by the method
190
+ # Array#create_stream.
142
191
  class CollectionStream < BasicStream
143
- attr_reader :pos
192
+ attr_reader :pos
144
193
 
145
- # Creates a new CollectionStream for the indexable sequence _seq_.
146
- def initialize(seq)
147
- @seq = seq
148
- set_to_begin
149
- end
194
+ # Creates a new CollectionStream for the indexable sequence _seq_.
195
+ def initialize(seq)
196
+ @seq = seq
197
+ set_to_begin
198
+ end
150
199
 
151
- def at_end?; @pos + 1 >= @seq.size; end
152
- def at_beginning?; @pos < 0; end
200
+ def at_end?
201
+ @pos + 1 >= @seq.size
202
+ end
203
+
204
+ def at_beginning?
205
+ @pos < 0
206
+ end
153
207
 
154
- # positioning
208
+ # positioning
209
+
210
+ def set_to_begin
211
+ @pos = -1
212
+ end
213
+
214
+ def set_to_end
215
+ @pos = @seq.size - 1
216
+ end
217
+
218
+ def basic_forward
219
+ @pos += 1
220
+ @seq[@pos]
221
+ end
155
222
 
156
- #
157
- def set_to_begin; @pos = -1; end
158
- def set_to_end; @pos = @seq.size - 1; end
223
+ def basic_backward
224
+ r = @seq[@pos]
225
+ @pos -= 1; r
226
+ end
159
227
 
160
- def basic_forward; @pos += 1; @seq[@pos]; end
161
- def basic_backward; r = @seq[@pos]; @pos -= 1; r; end
228
+ protected
162
229
 
163
- protected
230
+ # basic_current and basic_peek can be implemented more efficiently than in
231
+ # superclass
232
+ def basic_current
233
+ @seq[@pos]
234
+ end
164
235
 
165
- # basic_current and basic_peek can be implemented more efficiently than in
166
- # superclass
167
- def basic_current; @seq[@pos]; end
168
- def basic_peek; @seq[@pos+1]; end
236
+ def basic_peek
237
+ @seq[@pos + 1]
238
+ end
239
+ end
169
240
 
170
- end # CollectionStream
241
+ # CollectionStream
171
242
 
172
243
  # A simple Iterator for iterating over a sequence of integers starting from
173
- # zero up to a given upper bound. Mainly used by Stream::FilteredStream. Could be
174
- # made private but if somebody needs it here it is. Is there a better name for it?
244
+ # zero up to a given upper bound. Mainly used by Stream::FilteredStream. Could
245
+ # be made private but if somebody needs it here it is. Is there a better name
246
+ # for it?
175
247
  #
176
- # The upper bound is stored in the instance variable @stop which can be incremented
177
- # dynamically by the method increment_stop.
248
+ # The upper bound is stored in the instance variable @stop which can be
249
+ # incremented dynamically by the method increment_stop.
178
250
  class IntervalStream < BasicStream
179
- attr_reader :pos
251
+ attr_reader :pos
252
+
253
+ # Create a new IntervalStream with upper bound _stop_. stop - 1 is the last
254
+ # element. By default _stop_ is zero which means that the stream is empty.
255
+ def initialize(stop = 0)
256
+ @stop = stop - 1
257
+ set_to_begin
258
+ end
259
+
260
+ def at_beginning?
261
+ @pos < 0
262
+ end
263
+
264
+ def at_end?
265
+ @pos == @stop
266
+ end
180
267
 
181
- # Create a new IntervalStream with upper bound _stop_. stop - 1 is the last
182
- # element. By default _stop_ is zero which means that the stream is empty.
183
- def initialize (stop=0)
184
- @stop = stop - 1
185
- set_to_begin
186
- end
268
+ def set_to_end
269
+ @pos = @stop
270
+ end
187
271
 
188
- def at_beginning?; @pos < 0; end
189
- def at_end?; @pos == @stop; end
272
+ def set_to_begin
273
+ @pos = -1
274
+ end
190
275
 
191
- def set_to_end; @pos = @stop; end
192
- def set_to_begin; @pos = -1; end
276
+ # Increment the upper bound by incr.
277
+ def increment_stop(incr = 1)
278
+ @stop += incr
279
+ end
193
280
 
194
- # Increment the upper bound by incr.
195
- def increment_stop (incr=1); @stop += incr; end
281
+ def basic_forward
282
+ @pos += 1
283
+ end
196
284
 
197
- def basic_forward; @pos += 1; end
198
- def basic_backward; @pos -= 1; @pos + 1; end
285
+ def basic_backward
286
+ @pos -= 1
287
+ @pos + 1
288
+ end
199
289
  end
200
290
 
201
291
  # Class WrappedStream is the abstract superclass for stream classes that wrap
@@ -208,25 +298,41 @@ module Stream
208
298
  # arrayStream.to_a => [1,2,3]
209
299
  # Stream::WrappedStream.new(arrayStream).to_a => [1,2,3]
210
300
  class WrappedStream < BasicStream
211
- attr_reader :wrapped_stream
301
+ attr_reader :wrapped_stream
302
+
303
+ # Create a new WrappedStream wrapping the Stream _other_stream_.
304
+ def initialize(other_stream)
305
+ @wrapped_stream = other_stream
306
+ end
307
+
308
+ def at_beginning?
309
+ @wrapped_stream.at_beginning?
310
+ end
212
311
 
213
- # Create a new WrappedStream wrapping the Stream _otherStream_.
214
- def initialize (otherStream)
215
- @wrapped_stream = otherStream
216
- end
312
+ def at_end?
313
+ @wrapped_stream.at_end?
314
+ end
217
315
 
218
- def at_beginning?; @wrapped_stream.at_beginning?; end
219
- def at_end?; @wrapped_stream.at_end?; end
316
+ def set_to_end
317
+ @wrapped_stream.set_to_end
318
+ end
220
319
 
221
- def set_to_end; @wrapped_stream.set_to_end; end
222
- def set_to_begin; @wrapped_stream.set_to_begin; end
320
+ def set_to_begin
321
+ @wrapped_stream.set_to_begin
322
+ end
223
323
 
224
- # Returns the wrapped stream unwrapped.
225
- def unwrapped; @wrapped_stream.unwrapped; end
324
+ # Returns the wrapped stream unwrapped.
325
+ def unwrapped
326
+ @wrapped_stream.unwrapped
327
+ end
226
328
 
227
- public # but should be protected. Would like to have a friend concept here.
228
- def basic_forward; @wrapped_stream.basic_forward; end
229
- def basic_backward; @wrapped_stream.basic_backward; end
329
+ def basic_forward
330
+ @wrapped_stream.basic_forward
331
+ end
332
+
333
+ def basic_backward
334
+ @wrapped_stream.basic_backward
335
+ end
230
336
  end
231
337
 
232
338
  ##
@@ -237,213 +343,256 @@ module Stream
237
343
  #
238
344
  # (1..6).create_stream.filtered { |x| x % 2 == 0 }.to_a ==> [2, 4, 6]
239
345
  class FilteredStream < WrappedStream
346
+ # Create a new FilteredStream wrapping _other_stream_ and selecting all its
347
+ # elements which satisfy the condition defined by the block_filter_.
348
+ def initialize(other_stream, &filter)
349
+ super other_stream
350
+ @filter = filter
351
+ @position_holder = IntervalStream.new
352
+ set_to_begin
353
+ end
240
354
 
241
- # Create a new FilteredStream wrapping _otherStream_ and selecting all its
242
- # elements which satisfy the condition defined by the block_filter_.
243
- def initialize (otherStream, &filter)
244
- super otherStream
245
- @filter = filter
246
- @positionHolder = IntervalStream.new
247
- set_to_begin
248
- end
249
-
250
- def at_beginning?; @positionHolder.at_beginning?; end
251
-
252
- # at_end? has to look ahead if there is an element satisfing the filter
253
- def at_end?
254
- @positionHolder.at_end? and
255
- begin
256
- if @peek.nil?
257
- @peek = wrapped_stream.move_forward_until( &@filter ) or return true
258
- @positionHolder.increment_stop
259
- end
260
- false
261
- end
262
- end
263
-
264
- def basic_forward
265
- result =
266
- if @peek.nil?
267
- wrapped_stream.move_forward_until(&@filter)
268
- else
269
- # Do not move!!
270
- @peek
271
- end
272
- @peek = nil
273
- @positionHolder.forward
274
- result
275
- end
276
-
277
- def basic_backward
278
- wrapped_stream.backward unless @peek.nil?
279
- @peek = nil
280
- @positionHolder.backward
281
- wrapped_stream.move_backward_until(&@filter) or self
282
- end
283
-
284
- def set_to_end
285
- # Not super which is a WrappedStream, but same behavior as in Stream
286
- until at_end?; basic_forward; end
287
- end
288
-
289
- def set_to_begin
290
- super
291
- @peek = nil
292
- @positionHolder.set_to_begin
293
- end
294
-
295
- # Returns the current position of the stream.
296
- def pos; @positionHolder.pos; end
297
- end # FilteredStream
355
+ def at_beginning?
356
+ @position_holder.at_beginning?
357
+ end
358
+
359
+ # at_end? has to look ahead if there is an element satisfing the filter
360
+ def at_end?
361
+ @position_holder.at_end? and
362
+ begin
363
+ if @peek.nil?
364
+ @peek = wrapped_stream.move_forward_until(&@filter) or return true
365
+ @position_holder.increment_stop
366
+ end
367
+ false
368
+ end
369
+ end
370
+
371
+ def basic_forward
372
+ result =
373
+ if @peek.nil?
374
+ wrapped_stream.move_forward_until(&@filter)
375
+ else
376
+ # Do not move!!
377
+ @peek
378
+ end
379
+ @peek = nil
380
+ @position_holder.forward
381
+ result
382
+ end
383
+
384
+ def basic_backward
385
+ wrapped_stream.backward unless @peek.nil?
386
+ @peek = nil
387
+ @position_holder.backward
388
+ wrapped_stream.move_backward_until(&@filter) or self
389
+ end
390
+
391
+ def set_to_end
392
+ # Not super which is a WrappedStream, but same behavior as in Stream
393
+ basic_forward until at_end?
394
+ end
395
+
396
+ def set_to_begin
397
+ super
398
+ @peek = nil
399
+ @position_holder.set_to_begin
400
+ end
401
+
402
+ # Returns the current position of the stream.
403
+ def pos
404
+ @position_holder.pos
405
+ end
406
+ end
407
+
408
+ # FilteredStream
298
409
 
299
410
  ##
300
- # Each reversable stream (a stream that implements #backward and at_beginning?) can
301
- # be wrapped by a ReversedStream.
411
+ # Each reversable stream (a stream that implements #backward and
412
+ # at_beginning?) can be wrapped by a ReversedStream.
302
413
  #
303
414
  # A ReversedStream is created by the method #reverse:
304
415
  #
305
416
  # (1..6).create_stream.reverse.to_a ==> [6, 5, 4, 3, 2, 1]
306
417
  class ReversedStream < WrappedStream
418
+ # Create a reversing wrapper for the reversable stream _other_stream_. If
419
+ # _other_stream_ does not support backward moving a NotImplementedError is
420
+ # signaled on the first backward move.
421
+ def initialize(other_stream)
422
+ super other_stream
423
+ set_to_begin
424
+ end
425
+
426
+ # Returns true if the wrapped stream is at_end?.
427
+ def at_beginning?
428
+ wrapped_stream.at_end?
429
+ end
307
430
 
308
- # Create a reversing wrapper for the reversable stream _otherStream_. If
309
- # _otherStream_ does not support backward moving a NotImplementedError is signaled
310
- # on the first backward move.
311
- def initialize (otherStream)
312
- super otherStream
313
- set_to_begin
314
- end
431
+ # Returns true if the wrapped stream is at_beginning?.
432
+ def at_end?
433
+ wrapped_stream.at_beginning?
434
+ end
315
435
 
316
- # Returns true if the wrapped stream is at_end?.
317
- def at_beginning?; wrapped_stream.at_end?; end
318
- # Returns true if the wrapped stream is at_beginning?.
319
- def at_end?; wrapped_stream.at_beginning?; end
436
+ # Moves the wrapped stream one step backward.
437
+ def basic_forward
438
+ wrapped_stream.basic_backward
439
+ end
320
440
 
321
- # Moves the wrapped stream one step backward.
322
- def basic_forward; wrapped_stream.basic_backward; end
323
- # Moves the wrapped stream one step forward.
324
- def basic_backward; wrapped_stream.basic_forward; end
441
+ # Moves the wrapped stream one step forward.
442
+ def basic_backward
443
+ wrapped_stream.basic_forward
444
+ end
445
+
446
+ # Sets the wrapped stream to the beginning.
447
+ def set_to_end
448
+ wrapped_stream.set_to_begin
449
+ end
325
450
 
326
- # Sets the wrapped stream to the beginning.
327
- def set_to_end; wrapped_stream.set_to_begin; end
328
- # Sets the wrapped stream to the end.
329
- def set_to_begin; wrapped_stream.set_to_end; end
451
+ # Sets the wrapped stream to the end.
452
+ def set_to_begin
453
+ wrapped_stream.set_to_end
454
+ end
330
455
  end
331
456
 
332
457
  ##
333
- # The analog to Enumerable#collect for a stream is a MappedStream wrapping another
334
- # stream. A MappedStream is created by the method #collect, thus modifying
335
- # the behavior mixed in by Enumerable:
458
+ # The analog to Enumerable#collect for a stream is a MappedStream wrapping
459
+ # another stream. A MappedStream is created by the method #collect, thus
460
+ # modifying the behavior mixed in by Enumerable:
336
461
  #
337
462
  # (1..5).create_stream.collect {|x| x**2}.type ==> Stream::MappedStream
338
463
  # (1..5).collect {|x| x**2} ==> [1, 4, 9, 16, 25]
339
464
  # (1..5).create_stream.collect {|x| x**2}.to_a ==> [1, 4, 9, 16, 25]
340
465
  class MappedStream < WrappedStream
466
+ ##
467
+ # Creates a new MappedStream wrapping _other_stream_ which calls the block
468
+ # _mapping_ on each move.
469
+ def initialize(other_stream, &mapping)
470
+ super other_stream
471
+ @mapping = mapping
472
+ end
341
473
 
342
- ##
343
- # Creates a new MappedStream wrapping _otherStream_ which calls the block
344
- # _mapping_ on each move.
345
- def initialize (otherStream, &mapping)
346
- super otherStream
347
- @mapping = mapping
348
- end
474
+ # Apply the stored closure for the next element in the wrapped stream and
475
+ # return the result.
476
+ def basic_forward
477
+ @mapping.call(super)
478
+ end
349
479
 
350
- # Apply the stored closure for the next element in the wrapped stream and return
351
- # the result.
352
- def basic_forward; @mapping.call(super); end
353
- # Apply the stored closure for the previous element in the wrapped stream and return
354
- # the result.
355
- def basic_backward; @mapping.call(super); end
480
+ # Apply the stored closure for the previous element in the wrapped stream
481
+ # and return the result.
482
+ def basic_backward
483
+ @mapping.call(super)
484
+ end
356
485
  end
357
486
 
358
487
  ##
359
- # Given a stream of streams. Than a ConcatenatedStream is obtained by concatenating
360
- # these in the given order. A ConcatenatedStream is created by the methods
361
- # Stream#concatenate or Stream#concatenate_collected send to a stream of streams or
362
- # by the method + which concatenats two streams:
488
+ # Given a stream of streams. Than a ConcatenatedStream is obtained by
489
+ # concatenating these in the given order. A ConcatenatedStream is created by
490
+ # the methods Stream#concatenate or Stream#concatenate_collected send to a
491
+ # stream of streams or by the method + which concatenats two streams:
363
492
  #
364
493
  # ((1..3).create_stream + [4,5].create_stream).to_a ==> [1, 2, 3, 4, 5]
365
494
  class ConcatenatedStream < WrappedStream
366
- alias :streamOfStreams :wrapped_stream
367
- private :streamOfStreams
368
-
369
- # Creates a new ConcatenatedStream wrapping the stream of streams _streamOfStreams_.
370
- def initialize (streamOfStreams)
371
- super
372
- set_to_begin
373
- end
374
-
375
- # If the current stream is at end, than at_end? has to look ahead to find a non
376
- # empty in the stream of streams, which than gets the current stream.
377
- def at_end?
378
- @currentStream.at_end? and
379
- begin
380
- until streamOfStreams.at_end?
381
- dir, @dirOfLastMove = @dirOfLastMove, :forward
382
- s = streamOfStreams.basic_forward
383
- # if last move was backwards, then @currentStream is
384
- # equivalent to s. Move to next stream.
385
- next if dir == :backward
386
- s.set_to_begin
387
- if s.at_end? # empty stream?
388
- next # skip it
389
- else
390
- @currentStream = s
391
- return false # found non empty stream
392
- end
393
- end
394
- reachedBoundary # sets @dirOfLastMove and @currentStream
395
- end
396
- end
397
-
398
- # Same as at_end? the other way round.
399
- def at_beginning?
400
- # same algorithm as at_end? the other way round. Could we do it
401
- # with metaprogramming?
402
- @currentStream.at_beginning? and
403
- begin
404
- until streamOfStreams.at_beginning?
405
- dir, @dirOfLastMove = @dirOfLastMove, :backward
406
- s = streamOfStreams.basic_backward
407
- next if dir == :forward
408
- s.set_to_end
409
- if s.at_beginning?
410
- next
411
- else
412
- @currentStream = s
413
- return false
414
- end
415
- end
416
- reachedBoundary
417
- end
418
- end
419
-
420
- def set_to_begin; super; reachedBoundary end
421
- def set_to_end; super; reachedBoundary end
422
-
423
- # Returns the next element of @currentStream. at_end? ensured that there is one.
424
- def basic_forward; @currentStream.basic_forward end
425
- # Returns the previous element of @currentStream. at_beginning? ensured that
426
- # there is one.
427
- def basic_backward; @currentStream.basic_backward end
428
-
429
- private
430
-
431
- def reachedBoundary
432
- @currentStream = EmptyStream.instance
433
- @dirOfLastMove = :none # not :forward or :backward
434
- true
435
- end
436
- # Uff, this was the hardest stream to implement.
437
- end # ConcatenatedStream
438
-
439
- # An ImplicitStream is an easy way to create a stream on the fly without defining a
440
- # subclass of BasicStream. The basic methods required for a stream are defined with
441
- # blocks:
495
+ alias streamOfStreams wrapped_stream
496
+ private :streamOfStreams
497
+
498
+ # Creates a new ConcatenatedStream wrapping the stream of streams
499
+ # _streamOfStreams_.
500
+ def initialize(streamOfStreams)
501
+ super
502
+ set_to_begin
503
+ end
504
+
505
+ # If the current stream is at end, than at_end? has to look ahead to find a
506
+ # non empty in the stream of streams, which than gets the current stream.
507
+ def at_end?
508
+ unless @current_stream.at_end?
509
+ return false
510
+ end
511
+
512
+ until streamOfStreams.at_end?
513
+ dir = @dir_of_last_move
514
+ @dir_of_last_move = :forward
515
+ s = streamOfStreams.basic_forward
516
+ # if last move was backwards, then @current_stream is
517
+ # equivalent to s. Move to next stream.
518
+ next if dir == :backward
519
+
520
+ s.set_to_begin
521
+ if s.at_end? # empty stream?
522
+ next # skip it
523
+ else
524
+ @current_stream = s
525
+ return false # found non empty stream
526
+ end
527
+ end # until
528
+ reached_boundary # sets @dir_of_last_move and @current_stream
529
+ end
530
+
531
+ # Same as at_end? the other way round.
532
+ # @return [Boolean]
533
+ def at_beginning?
534
+ # same algorithm as at_end? the other way round.
535
+ unless @current_stream.at_beginning?
536
+ return false
537
+ end
538
+
539
+ until streamOfStreams.at_beginning?
540
+ dir = @dir_of_last_move
541
+ @dir_of_last_move = :backward
542
+ s = streamOfStreams.basic_backward
543
+ next if dir == :forward
544
+
545
+ s.set_to_end
546
+ if s.at_beginning?
547
+ next
548
+ else
549
+ @current_stream = s
550
+ return false
551
+ end
552
+ end
553
+ reached_boundary
554
+ end
555
+
556
+ def set_to_begin
557
+ super; reached_boundary
558
+ end
559
+
560
+ def set_to_end
561
+ super; reached_boundary
562
+ end
563
+
564
+ # Returns the next element of @current_stream. at_end? ensured that there is
565
+ # one.
566
+ def basic_forward
567
+ @current_stream.basic_forward
568
+ end
569
+
570
+ # Returns the previous element of @current_stream. at_beginning? ensured that
571
+ # there is one.
572
+ def basic_backward
573
+ @current_stream.basic_backward
574
+ end
575
+
576
+ private
577
+
578
+ def reached_boundary
579
+ @current_stream = EmptyStream.instance
580
+ @dir_of_last_move = :none # not :forward or :backward
581
+ true
582
+ end
583
+ # Uff, this was the hardest stream to implement.
584
+ end
585
+
586
+ # ConcatenatedStream
587
+
588
+ # An ImplicitStream is an easy way to create a stream on the fly without
589
+ # defining a subclass of BasicStream. The basic methods required for a stream
590
+ # are defined with blocks:
442
591
  #
443
592
  # s = Stream::ImplicitStream.new { |s|
444
593
  # x = 0
445
- # s.at_end_proc = proc {x == 5}
446
- # s.forward_proc = proc {x += 1 }
594
+ # s.at_end_proc = proc { x == 5 }
595
+ # s.forward_proc = proc { x += 1 }
447
596
  # }
448
597
  #
449
598
  # s.to_a ==> [1, 2, 3, 4, 5]
@@ -457,111 +606,145 @@ module Stream
457
606
  # remove the first or last element of an existing stream (see remove_first
458
607
  # and remove_last).
459
608
  class ImplicitStream < BasicStream
460
- attr_writer :at_beginning_proc, :at_end_proc, :forward_proc, :backward_proc, :set_to_begin_proc, :set_to_end_proc
461
- attr_reader :wrapped_stream
462
-
463
- # Create a new ImplicitStream which might wrap an existing stream
464
- # _otherStream_. If _otherStream_ is supplied the blocks for the basic stream
465
- # methods are initialized with closures that delegate all operations to the
466
- # wrapped stream.
467
- #
468
- # If a block is given to new, than it is called with the new ImplicitStream
469
- # stream as parameter letting the client overwriting the default blocks.
470
- def initialize (otherStream=nil)
471
- if otherStream
472
- @wrapped_stream = otherStream
473
- @at_beginning_proc = proc {otherStream.at_beginning?}
474
- @at_end_proc = proc {otherStream.at_end?}
475
- @forward_proc = proc {otherStream.basic_forward}
476
- @backward_proc = proc {otherStream.basic_backward}
477
- @set_to_end_proc = proc {otherStream.set_to_end}
478
- @set_to_begin_proc = proc {otherStream.set_to_begin}
479
- end
480
- yield self if block_given? # let client overwrite defaults
481
-
482
- @at_beginning_proc = proc {true} unless @at_beginning_proc
483
- @at_end_proc = proc {true} unless @at_end_proc
484
- end
485
-
486
- # Returns the value of @at_beginning_proc.
487
- def at_beginning?; @at_beginning_proc.call; end
488
- # Returns the value of @at_end_proc.
489
- def at_end?; @at_end_proc.call; end
490
-
491
- # Returns the value of @forward_proc.
492
- def basic_forward; @forward_proc.call; end
493
- # Returns the value of @backward_proc_proc.
494
- def basic_backward; @backward_proc.call; end
495
-
496
- # Calls set_to_end_proc or super if set_to_end_proc is undefined.
497
- def set_to_end
498
- @set_to_end_proc ? @set_to_end_proc.call : super
499
- end
500
-
501
- # Calls set_to_begin_proc or super if set_to_begin_proc is undefined.
502
- def set_to_begin
503
- @set_to_begin_proc ? @set_to_begin_proc.call : super
504
- end
505
- end # ImplicitStream
609
+ attr_writer :at_beginning_proc, :at_end_proc, :forward_proc,
610
+ :backward_proc, :set_to_begin_proc, :set_to_end_proc
611
+ attr_reader :wrapped_stream
612
+
613
+ # Create a new ImplicitStream which might wrap an existing stream
614
+ # _other_stream_. If _other_stream_ is supplied the blocks for the basic
615
+ # stream methods are initialized with closures that delegate all operations
616
+ # to the wrapped stream.
617
+ #
618
+ # If a block is given to new, than it is called with the new ImplicitStream
619
+ # stream as parameter letting the client overwriting the default blocks.
620
+ def initialize(other_stream = nil)
621
+ # Initialize with defaults
622
+ @at_beginning_proc = proc { true }
623
+ @at_end_proc = proc { true }
624
+
625
+ @set_to_begin_proc = proc {}
626
+ @set_to_end_proc = proc {}
627
+
628
+ if other_stream
629
+ @wrapped_stream = other_stream
630
+ @at_beginning_proc = proc { other_stream.at_beginning? }
631
+ @at_end_proc = proc { other_stream.at_end? }
632
+ @forward_proc = proc { other_stream.basic_forward }
633
+ @backward_proc = proc { other_stream.basic_backward }
634
+ @set_to_end_proc = proc { other_stream.set_to_end }
635
+ @set_to_begin_proc = proc { other_stream.set_to_begin }
636
+ end
637
+ yield self if block_given? # let client overwrite defaults
638
+ end
639
+
640
+ # Returns the value of @at_beginning_proc.
641
+ def at_beginning?
642
+ @at_beginning_proc.call
643
+ end
644
+
645
+ # Returns the value of @at_end_proc.
646
+ def at_end?
647
+ @at_end_proc.call
648
+ end
649
+
650
+ # Returns the value of @forward_proc.
651
+ def basic_forward
652
+ @forward_proc.call
653
+ end
654
+
655
+ # Returns the value of @backward_proc_proc.
656
+ def basic_backward
657
+ @backward_proc.call
658
+ end
659
+
660
+ # Calls set_to_end_proc or super if set_to_end_proc is undefined.
661
+ def set_to_end
662
+ @set_to_end_proc ? @set_to_end_proc.call : super
663
+ end
664
+
665
+ # Calls set_to_begin_proc or super if set_to_begin_proc is undefined.
666
+ def set_to_begin
667
+ @set_to_begin_proc ? @set_to_begin_proc.call : super
668
+ end
669
+ end
670
+
671
+ # ImplicitStream
506
672
 
507
673
  # Stream creation functions
508
674
 
509
675
  ##
510
676
  # Return a Stream::FilteredStream which iterates over all my elements
511
677
  # satisfying the condition specified by the block.
512
- def filtered (&block); FilteredStream.new(self,&block); end
678
+ def filtered(&block)
679
+ FilteredStream.new(self, &block)
680
+ end
513
681
 
514
682
  # Create a Stream::ReversedStream wrapper on self.
515
- def reverse; ReversedStream.new self; end
516
-
517
- # Create a Stream::MappedStream wrapper on self. Instead of returning the stream
518
- # element on each move, the value of calling _mapping_ is returned instead. See
519
- # Stream::MappedStream for examples.
520
- def collect (&mapping); MappedStream.new(self, &mapping); end
521
-
522
- # Create a Stream::ConcatenatedStream on self, which must be a stream of streams.
523
- def concatenate; ConcatenatedStream.new self; end
524
-
525
- # Create a Stream::ConcatenatedStream, concatenated from streams build with the
526
- # block for each element of self:
527
- #
683
+ def reverse
684
+ ReversedStream.new self
685
+ end
686
+
687
+ # Create a Stream::MappedStream wrapper on self. Instead of returning the
688
+ # stream element on each move, the value of calling _mapping_ is returned
689
+ # instead. See Stream::MappedStream for examples.
690
+ def collect(&mapping)
691
+ MappedStream.new(self, &mapping)
692
+ end
693
+
694
+ # Create a Stream::ConcatenatedStream on self, which must be a stream of
695
+ # streams.
696
+ def concatenate
697
+ ConcatenatedStream.new self
698
+ end
699
+
700
+ # Create a Stream::ConcatenatedStream, concatenated from streams build with
701
+ # the block for each element of self:
702
+ #
528
703
  # s = [1, 2, 3].create_stream.concatenate_collected { |i|
529
704
  # [i,-i].create_stream
530
705
  # }.
531
706
  # s.to_a ==> [1, -1, 2, -2, 3, -3]
532
- def concatenate_collected (&mapping); self.collect(&mapping).concatenate; end
707
+ def concatenate_collected(&mapping)
708
+ collect(&mapping).concatenate
709
+ end
533
710
 
534
- # Create a Stream::ConcatenatedStream by concatenatating the receiver and _otherStream_
711
+ # Create a Stream::ConcatenatedStream by concatenatating the receiver and
712
+ # _other_stream_
535
713
  #
536
- # (%w(a b c).create_stream + [4,5].create_stream).to_a ==> ["a", "b", "c", 4, 5]
537
- def + (otherStream)
538
- [self, otherStream].create_stream.concatenate
714
+ # (%w(a b c).create_stream + [4,5].create_stream).to_a
715
+ # ==> ["a", "b", "c", 4, 5]
716
+ def +(other)
717
+ [self, other].create_stream.concatenate
539
718
  end
540
719
 
541
- # Create a Stream::ImplicitStream which wraps the receiver stream by modifying one
542
- # or more basic methods of the receiver. As an example the method remove_first uses
543
- # #modify to create an ImplicitStream which filters the first element away.
544
- def modify (&block); ImplicitStream.new(self, &block); end
720
+ # Create a Stream::ImplicitStream which wraps the receiver stream by modifying
721
+ # one or more basic methods of the receiver. As an example the method
722
+ # remove_first uses #modify to create an ImplicitStream which filters the
723
+ # first element away.
724
+ def modify(&block)
725
+ ImplicitStream.new(self, &block)
726
+ end
545
727
 
546
728
  # Returns a Stream::ImplicitStream wrapping a Stream::FilteredStream, which
547
729
  # eliminates the first element of the receiver.
548
730
  #
549
731
  # (1..3).create_stream.remove_first.to_a ==> [2,3]
550
732
  def remove_first
551
- i = 0
552
- filter = self.filtered { | element | i += 1; i > 1 }
553
- filter.modify { |s|
554
- s.set_to_begin_proc = proc {filter.set_to_begin; i = 0}
555
- }
733
+ i = 0
734
+ filter = filtered { i += 1; i > 1 }
735
+ filter.modify do |s|
736
+ s.set_to_begin_proc = proc { filter.set_to_begin; i = 0 }
737
+ end
556
738
  end
557
739
 
558
740
  # Returns a Stream which eliminates the first element of the receiver.
559
741
  #
560
742
  # (1..3).create_stream.remove_last.to_a ==> [1,2]
561
743
  #
562
- # <em>Take a look at the source. The implementation is inefficient but elegant.</em>
744
+ # <em>Take a look at the source. The implementation is inefficient but
745
+ # elegant.</em>
563
746
  def remove_last
564
- self.reverse.remove_first.reverse # I like this one
747
+ reverse.remove_first.reverse # I like this one
565
748
  end
566
749
  end
567
750