stream 0.5

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.
@@ -0,0 +1,22 @@
1
+ # Let a Generator look like a Stream.
2
+ require 'stream'
3
+ require 'generator'
4
+
5
+ # The generator is made a Stream by aliasing
6
+ # the methods at_end?, basic_forward, basic_peek, set_to_begin to the approriate
7
+ # methods #end?, #next, #peek and #rewind of the Generator class.
8
+ #
9
+ # Be careful if you already use a version of Akinori MUSHAs generator.rb. Check out
10
+ # the version numbers of the one you use and the one comming with the stream package.
11
+ class Generator
12
+ include Stream
13
+
14
+ alias_method :at_end?, :end?
15
+ alias_method :basic_forward, :next
16
+ alias_method :basic_peek, :peek
17
+ alias_method :set_to_begin, :rewind
18
+
19
+ # Returns the generator itself.
20
+ def create_stream; self; end
21
+ end
22
+
@@ -0,0 +1,585 @@
1
+ STREAM_VERSION = "0.5"
2
+
3
+ ##
4
+ # Module Stream defines an interface for an external Iterator which
5
+ # can move forward and backwards. See README for more information.
6
+ #
7
+ # The functionality is similar to Smalltalk's ReadStream.
8
+
9
+ module Stream
10
+ include Enumerable
11
+
12
+ # This exception is raised when the Stream is requested to move past
13
+ # the end or beginning.
14
+ class EndOfStreamException < StandardError; end
15
+
16
+ # Returns false if the next #forward will return an element.
17
+ def at_end?; raise NotImplementedError; end
18
+
19
+ # Returns false if the next #backward will return an element.
20
+ def at_beginning?; raise NotImplementedError; end
21
+
22
+ # Move forward one position. Returns the _target_ of current_edge.
23
+ # Raises Stream::EndOfStreamException if at_end? is true.
24
+ def forward
25
+ raise EndOfStreamException if at_end?
26
+ basic_forward
27
+ end
28
+
29
+ # Move backward one position. Returns the _source_ of current_edge. Raises
30
+ # Stream::EndOfStreamException if at_beginning? is true.
31
+ def backward
32
+ raise EndOfStreamException if at_beginning?
33
+ basic_backward
34
+ end
35
+
36
+ # Position the stream before its first element, i.e. the next #forward
37
+ # will return the first element.
38
+ def set_to_begin
39
+ until at_beginning?; basic_backward; end
40
+ end
41
+
42
+ # Position the stream behind its last element, i.e. the next #backward
43
+ # will return the last element.
44
+ def set_to_end
45
+ until at_end?; basic_forward; end
46
+ end
47
+
48
+ protected
49
+
50
+ def basic_forward; raise NotImplementedError; end
51
+ def basic_backward; raise NotImplementedError; end
52
+
53
+ def basic_current; backward; forward; end
54
+ def basic_peek; forward; backward; end
55
+
56
+ public
57
+
58
+ # Move forward until the boolean block is not false and returns the element
59
+ # found. Returns nil if no object matches.
60
+ #
61
+ # This is similar to #detect, but starts the search from the
62
+ # current position. #detect, which is inherited from Enumerable uses
63
+ # #each, which implicitly calls #set_to_begin.
64
+ def move_forward_until
65
+ until at_end?
66
+ element = basic_forward
67
+ return element if yield(element)
68
+ end
69
+ nil
70
+ end
71
+
72
+ # Move backward until the boolean block is not false and returns the element
73
+ # found. Returns nil if no object matches.
74
+ 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
85
+
86
+ # Returns the element returned by the last call of #backward. If at_end? is
87
+ # true self is returned.
88
+ def peek; at_end? ? self : basic_peek; end
89
+
90
+ # Returns the array [#current,#peek].
91
+ def current_edge; [current,peek]; end
92
+
93
+ # Returns the first element of the stream. This is accomplished by calling
94
+ # set_to_begin and #forward, which means a state change.
95
+ def first; set_to_begin; forward; end
96
+
97
+ # Returns the last element of the stream. This is accomplished by calling
98
+ # set_to_begin and #backward, which means a state change.
99
+ def last; set_to_end; backward; end
100
+
101
+ # Returns true if the stream is empty which is equivalent to at_end? and
102
+ # at_beginning? both being true.
103
+ def empty?; at_end? and at_beginning?; end
104
+
105
+ # Implements the standard iterator used by module Enumerable, by calling
106
+ # set_to_begin and basic_forward until at_end? is true.
107
+ def each
108
+ set_to_begin
109
+ until at_end?
110
+ yield basic_forward
111
+ end
112
+ 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
+
118
+ # A Stream::WrappedStream should return the wrapped stream unwrapped. If the
119
+ # stream is not a wrapper around another stream it simply returns itself.
120
+ def unwrapped; self; end
121
+
122
+ # The abstract super class of all concrete Classes implementing the Stream
123
+ # interface. Only used for including module Stream.
124
+ class BasicStream
125
+ include Stream
126
+ end
127
+
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?
130
+ class EmptyStream < BasicStream
131
+ require 'singleton'
132
+ include Singleton
133
+
134
+ def at_end?; true; end
135
+ def at_beginning?; true; end
136
+ end
137
+
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.
140
+ #
141
+ # A CollectionStream for an array is created by the method Array#create_stream.
142
+ class CollectionStream < BasicStream
143
+ attr_reader :pos
144
+
145
+ # Creates a new CollectionStream for the indexable sequence _seq_.
146
+ def initialize(seq)
147
+ @seq = seq
148
+ set_to_begin
149
+ end
150
+
151
+ def at_end?; @pos + 1 >= @seq.size; end
152
+ def at_beginning?; @pos < 0; end
153
+
154
+ # positioning
155
+
156
+ #
157
+ def set_to_begin; @pos = -1; end
158
+ def set_to_end; @pos = @seq.size - 1; end
159
+
160
+ def basic_forward; @pos += 1; @seq[@pos]; end
161
+ def basic_backward; r = @seq[@pos]; @pos -= 1; r; end
162
+
163
+ protected
164
+
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
169
+
170
+ end # CollectionStream
171
+
172
+ # 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?
175
+ #
176
+ # The upper bound is stored in the instance variable @stop which can be incremented
177
+ # dynamically by the method increment_stop.
178
+ class IntervalStream < BasicStream
179
+ attr_reader :pos
180
+
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
187
+
188
+ def at_beginning?; @pos < 0; end
189
+ def at_end?; @pos == @stop; end
190
+
191
+ def set_to_end; @pos = @stop; end
192
+ def set_to_begin; @pos = -1; end
193
+
194
+ # Increment the upper bound by incr.
195
+ def increment_stop (incr=1); @stop += incr; end
196
+
197
+ def basic_forward; @pos += 1; end
198
+ def basic_backward; @pos -= 1; @pos + 1; end
199
+ end
200
+
201
+ # Class WrappedStream is the abstract superclass for stream classes that wrap
202
+ # another stream. The basic methods are simple delegated to the wrapped
203
+ # stream. Thus creating a WrappedStream on a CollectionStream would yield an
204
+ # equivalent stream:
205
+ #
206
+ # arrayStream = [1,2,3].create_stream
207
+ #
208
+ # arrayStream.to_a => [1,2,3]
209
+ # Stream::WrappedStream.new(arrayStream).to_a => [1,2,3]
210
+ class WrappedStream < BasicStream
211
+ attr_reader :wrapped_stream
212
+
213
+ # Create a new WrappedStream wrapping the Stream _otherStream_.
214
+ def initialize (otherStream)
215
+ @wrapped_stream = otherStream
216
+ end
217
+
218
+ def at_beginning?; @wrapped_stream.at_beginning?; end
219
+ def at_end?; @wrapped_stream.at_end?; end
220
+
221
+ def set_to_end; @wrapped_stream.set_to_end; end
222
+ def set_to_begin; @wrapped_stream.set_to_begin; end
223
+
224
+ # Returns the wrapped stream unwrapped.
225
+ def unwrapped; @wrapped_stream.unwrapped; end
226
+
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
230
+ end
231
+
232
+ ##
233
+ # A FilteredStream selects all elements which satisfy a given booelan block of
234
+ # another stream being wrapped.
235
+ #
236
+ # A FilteredStream is created by the method #filtered:
237
+ #
238
+ # (1..6).create_stream.filtered { |x| x % 2 == 0 }.to_a ==> [2, 4, 6]
239
+ class FilteredStream < WrappedStream
240
+
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
298
+
299
+ ##
300
+ # Each reversable stream (a stream that implements #backward and at_beginning?) can
301
+ # be wrapped by a ReversedStream.
302
+ #
303
+ # A ReversedStream is created by the method #reverse:
304
+ #
305
+ # (1..6).create_stream.reverse.to_a ==> [6, 5, 4, 3, 2, 1]
306
+ class ReversedStream < WrappedStream
307
+
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
315
+
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
320
+
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
325
+
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
330
+ end
331
+
332
+ ##
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:
336
+ #
337
+ # (1..5).create_stream.collect {|x| x**2}.type ==> Stream::MappedStream
338
+ # (1..5).collect {|x| x**2} ==> [1, 4, 9, 16, 25]
339
+ # (1..5).create_stream.collect {|x| x**2}.to_a ==> [1, 4, 9, 16, 25]
340
+ class MappedStream < WrappedStream
341
+
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
349
+
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
356
+ end
357
+
358
+ ##
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:
363
+ #
364
+ # ((1..3).create_stream + [4,5].create_stream).to_a ==> [1, 2, 3, 4, 5]
365
+ 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:
442
+ #
443
+ # s = Stream::ImplicitStream.new { |s|
444
+ # x = 0
445
+ # s.at_end_proc = proc {x == 5}
446
+ # s.forward_proc = proc {x += 1 }
447
+ # }
448
+ #
449
+ # s.to_a ==> [1, 2, 3, 4, 5]
450
+ #
451
+ # Note that this stream is only partially defined since backward_proc and
452
+ # at_beginning_proc are not defined. It may as well be useful if only moving
453
+ # forward is required by the code fragment.
454
+ #
455
+ # ImplicitStreams can be based on other streams using the method modify
456
+ # which is for example used in the methods for creating stream wrappers which
457
+ # remove the first or last element of an existing stream (see remove_first
458
+ # and remove_last).
459
+ 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
506
+
507
+ # Stream creation functions
508
+
509
+ ##
510
+ # Return a Stream::FilteredStream which iterates over all my elements
511
+ # satisfying the condition specified by the block.
512
+ def filtered (&block); FilteredStream.new(self,&block); end
513
+
514
+ # 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
+ #
528
+ # s = [1, 2, 3].create_stream.concatenate_collected { |i|
529
+ # [i,-i].create_stream
530
+ # }.
531
+ # s.to_a ==> [1, -1, 2, -2, 3, -3]
532
+ def concatenate_collected (&mapping); self.collect(&mapping).concatenate; end
533
+
534
+ # Create a Stream::ConcatenatedStream by concatenatating the receiver and _otherStream_
535
+ #
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
539
+ end
540
+
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
545
+
546
+ # Returns a Stream::ImplicitStream wrapping a Stream::FilteredStream, which
547
+ # eliminates the first element of the receiver.
548
+ #
549
+ # (1..3).create_stream.remove_first.to_a ==> [2,3]
550
+ 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
+ }
556
+ end
557
+
558
+ # Returns a Stream which eliminates the first element of the receiver.
559
+ #
560
+ # (1..3).create_stream.remove_last.to_a ==> [1,2]
561
+ #
562
+ # <em>Take a look at the source. The implementation is inefficient but elegant.</em>
563
+ def remove_last
564
+ self.reverse.remove_first.reverse # I like this one
565
+ end
566
+ end
567
+
568
+ # extensions
569
+
570
+ # The extension on Array could be done for all Objects supporting []
571
+ # and size.
572
+ class Array
573
+ # Creates a new Stream::CollectionStream on self.
574
+ def create_stream
575
+ Stream::CollectionStream.new self
576
+ end
577
+ end
578
+
579
+ module Enumerable
580
+ # If not an array the enumerable is converted to an array and then
581
+ # to a stream using a Stream::CollectionStream.
582
+ def create_stream
583
+ to_a.create_stream
584
+ end
585
+ end