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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +30 -0
- data/LICENSE +56 -0
- data/{README → README.rdoc} +6 -8
- data/Rakefile +23 -134
- data/lib/stream.rb +535 -352
- data/test/test_helper.rb +2 -0
- data/test/teststream.rb +111 -101
- metadata +112 -48
- data/install.rb +0 -24
- data/lib/generator2stream.rb +0 -22
- data/test/bm.rb +0 -49
- data/test/testgenerator.rb +0 -24
data/lib/stream.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
STREAM_VERSION =
|
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
|
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
|
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
|
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
|
51
|
+
basic_forward until at_end?
|
46
52
|
end
|
47
53
|
|
48
54
|
protected
|
49
55
|
|
50
|
-
def basic_forward
|
51
|
-
|
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
|
54
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
# Returns the element returned by the last call of #forward. If at_beginning?
|
83
|
-
# true self is returned.
|
84
|
-
def current
|
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
|
108
|
+
def peek
|
109
|
+
at_end? ? self : basic_peek
|
110
|
+
end
|
89
111
|
|
90
112
|
# Returns the array [#current,#peek].
|
91
|
-
def current_edge
|
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
|
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
|
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
|
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
|
-
|
111
|
-
|
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
|
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
|
-
|
159
|
+
include Stream
|
126
160
|
end
|
127
161
|
|
128
|
-
# A Singleton class for an empty stream. EmptyStream.instance is the sole
|
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
|
-
|
132
|
-
|
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
|
-
|
135
|
-
|
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
|
139
|
-
# collection. The state of the iterator is stored in instance
|
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
|
189
|
+
# A CollectionStream for an array is created by the method
|
190
|
+
# Array#create_stream.
|
142
191
|
class CollectionStream < BasicStream
|
143
|
-
|
192
|
+
attr_reader :pos
|
144
193
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
-
|
152
|
-
|
200
|
+
def at_end?
|
201
|
+
@pos + 1 >= @seq.size
|
202
|
+
end
|
203
|
+
|
204
|
+
def at_beginning?
|
205
|
+
@pos < 0
|
206
|
+
end
|
153
207
|
|
154
|
-
|
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
|
-
|
158
|
-
|
223
|
+
def basic_backward
|
224
|
+
r = @seq[@pos]
|
225
|
+
@pos -= 1; r
|
226
|
+
end
|
159
227
|
|
160
|
-
|
161
|
-
def basic_backward; r = @seq[@pos]; @pos -= 1; r; end
|
228
|
+
protected
|
162
229
|
|
163
|
-
|
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
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
236
|
+
def basic_peek
|
237
|
+
@seq[@pos + 1]
|
238
|
+
end
|
239
|
+
end
|
169
240
|
|
170
|
-
|
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
|
174
|
-
# made private but if somebody needs it here it is. Is there a better name
|
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
|
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
|
-
|
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
|
-
|
182
|
-
|
183
|
-
|
184
|
-
@stop = stop - 1
|
185
|
-
set_to_begin
|
186
|
-
end
|
268
|
+
def set_to_end
|
269
|
+
@pos = @stop
|
270
|
+
end
|
187
271
|
|
188
|
-
|
189
|
-
|
272
|
+
def set_to_begin
|
273
|
+
@pos = -1
|
274
|
+
end
|
190
275
|
|
191
|
-
|
192
|
-
|
276
|
+
# Increment the upper bound by incr.
|
277
|
+
def increment_stop(incr = 1)
|
278
|
+
@stop += incr
|
279
|
+
end
|
193
280
|
|
194
|
-
|
195
|
-
|
281
|
+
def basic_forward
|
282
|
+
@pos += 1
|
283
|
+
end
|
196
284
|
|
197
|
-
|
198
|
-
|
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
|
-
|
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
|
-
|
214
|
-
|
215
|
-
|
216
|
-
end
|
312
|
+
def at_end?
|
313
|
+
@wrapped_stream.at_end?
|
314
|
+
end
|
217
315
|
|
218
|
-
|
219
|
-
|
316
|
+
def set_to_end
|
317
|
+
@wrapped_stream.set_to_end
|
318
|
+
end
|
220
319
|
|
221
|
-
|
222
|
-
|
320
|
+
def set_to_begin
|
321
|
+
@wrapped_stream.set_to_begin
|
322
|
+
end
|
223
323
|
|
224
|
-
|
225
|
-
|
324
|
+
# Returns the wrapped stream unwrapped.
|
325
|
+
def unwrapped
|
326
|
+
@wrapped_stream.unwrapped
|
327
|
+
end
|
226
328
|
|
227
|
-
|
228
|
-
|
229
|
-
|
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
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
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
|
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
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
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
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
436
|
+
# Moves the wrapped stream one step backward.
|
437
|
+
def basic_forward
|
438
|
+
wrapped_stream.basic_backward
|
439
|
+
end
|
320
440
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
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
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
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
|
334
|
-
# stream. A MappedStream is created by the method #collect, thus
|
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
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
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
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
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
|
360
|
-
# these in the given order. A ConcatenatedStream is created by
|
361
|
-
# Stream#concatenate or Stream#concatenate_collected send to a
|
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
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
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
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
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
|
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
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
# Stream::MappedStream
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
#
|
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
|
707
|
+
def concatenate_collected(&mapping)
|
708
|
+
collect(&mapping).concatenate
|
709
|
+
end
|
533
710
|
|
534
|
-
# Create a Stream::ConcatenatedStream by concatenatating the receiver and
|
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
|
537
|
-
|
538
|
-
|
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
|
542
|
-
# or more basic methods of the receiver. As an example the method
|
543
|
-
# #modify to create an ImplicitStream which filters the
|
544
|
-
|
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
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
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
|
744
|
+
# <em>Take a look at the source. The implementation is inefficient but
|
745
|
+
# elegant.</em>
|
563
746
|
def remove_last
|
564
|
-
|
747
|
+
reverse.remove_first.reverse # I like this one
|
565
748
|
end
|
566
749
|
end
|
567
750
|
|