stream 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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