stream 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +0 -2
- data/Gemfile.lock +5 -2
- data/LICENSE +56 -0
- data/README.rdoc +1 -1
- data/Rakefile +2 -2
- data/lib/stream.rb +474 -303
- data/test/test_helper.rb +0 -3
- data/test/teststream.rb +111 -101
- metadata +22 -10
- data/lib/generator2stream.rb +0 -22
- data/test/bm.rb +0 -49
- data/test/testgenerator.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbfc5988a8fe8b8e4735b9f576da07e6e1aa18a87b4b8a6df0c917b5a9789a13
|
4
|
+
data.tar.gz: 06d3fe1334d5adbce1123621410c22efab33159396e967eb96d18d926c8c9080
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 161659d0943d54ca3c0ab0eadddf9933f800d57108b0f3f1368929fdc01bebb3dbb0cf12bc1cff8b82cffb005b7a1e04d2646a1ea628b692dd6890afa576310c
|
7
|
+
data.tar.gz: 55f2c790ea82ef64f90bdef541303ec4a2fc6be1292a19a96648f5bfc1159cd17e807d1264d1d98c55a6b5298129037c5576cd7b94f22931686cdd57e1e08f4f
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
stream (0.5.
|
4
|
+
stream (0.5.3)
|
5
|
+
generator
|
5
6
|
|
6
7
|
GEM
|
7
8
|
remote: https://rubygems.org/
|
@@ -9,6 +10,7 @@ GEM
|
|
9
10
|
codeclimate-test-reporter (1.0.9)
|
10
11
|
simplecov (<= 0.13)
|
11
12
|
docile (1.1.5)
|
13
|
+
generator (0.0.1)
|
12
14
|
json (2.1.0)
|
13
15
|
power_assert (1.1.3)
|
14
16
|
rake (12.3.2)
|
@@ -24,6 +26,7 @@ GEM
|
|
24
26
|
|
25
27
|
PLATFORMS
|
26
28
|
ruby
|
29
|
+
x86_64-linux
|
27
30
|
|
28
31
|
DEPENDENCIES
|
29
32
|
codeclimate-test-reporter
|
@@ -34,4 +37,4 @@ DEPENDENCIES
|
|
34
37
|
yard
|
35
38
|
|
36
39
|
BUNDLED WITH
|
37
|
-
2.
|
40
|
+
2.2.1
|
data/LICENSE
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
|
2
|
+
You can redistribute it and/or modify it under either the terms of the
|
3
|
+
2-clause BSDL (see the file BSDL), or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
13
|
+
make them Freely Available, such as by posting said
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
15
|
+
the author to include your modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) give non-standard binaries non-standard names, with
|
21
|
+
instructions on where to get the original software distribution.
|
22
|
+
|
23
|
+
d) make other distribution arrangements with the author.
|
24
|
+
|
25
|
+
3. You may distribute the software in object code or binary form,
|
26
|
+
provided that you do at least ONE of the following:
|
27
|
+
|
28
|
+
a) distribute the binaries and library files of the software,
|
29
|
+
together with instructions (in the manual page or equivalent)
|
30
|
+
on where to get the original distribution.
|
31
|
+
|
32
|
+
b) accompany the distribution with the machine-readable source of
|
33
|
+
the software.
|
34
|
+
|
35
|
+
c) give non-standard binaries non-standard names, with
|
36
|
+
instructions on where to get the original software distribution.
|
37
|
+
|
38
|
+
d) make other distribution arrangements with the author.
|
39
|
+
|
40
|
+
4. You may modify and include the part of the software into any other
|
41
|
+
software (possibly commercial). But some files in the distribution
|
42
|
+
are not written by the author, so that they are not under these terms.
|
43
|
+
|
44
|
+
For the list of those files and their copying conditions, see the
|
45
|
+
file LEGAL.
|
46
|
+
|
47
|
+
5. The scripts and library files supplied as input to or produced as
|
48
|
+
output from the software do not automatically fall under the
|
49
|
+
copyright of the software, but belong to whomever generated them,
|
50
|
+
and may be sold commercially, and may be aggregated with this
|
51
|
+
software.
|
52
|
+
|
53
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
54
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
55
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
56
|
+
PURPOSE.
|
data/README.rdoc
CHANGED
data/Rakefile
CHANGED
data/lib/stream.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
STREAM_VERSION =
|
1
|
+
STREAM_VERSION = '0.5.3'.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,77 +80,106 @@ 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
|
-
|
93
|
+
until at_beginning?
|
94
|
+
element = basic_backward
|
95
|
+
return element if yield(element)
|
96
|
+
end
|
97
|
+
nil
|
80
98
|
end
|
81
99
|
|
82
100
|
# Returns the element returned by the last call of #forward. If at_beginning?
|
83
101
|
# is true self is returned.
|
84
|
-
def current
|
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
|
-
yield basic_forward
|
111
|
-
end
|
141
|
+
yield basic_forward until at_end?
|
112
142
|
end
|
113
143
|
|
114
144
|
# create_stream is used for each Enumerable to create a stream for it. A
|
115
145
|
# Stream as an Enumerable returns itself.
|
116
|
-
def create_stream
|
146
|
+
def create_stream
|
147
|
+
self
|
148
|
+
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
162
|
# A Singleton class for an empty stream. EmptyStream.instance is the sole
|
129
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
185
|
# A CollectionStream can be used as an external iterator for each
|
@@ -142,34 +189,56 @@ module Stream
|
|
142
189
|
# A CollectionStream for an array is created by the method
|
143
190
|
# Array#create_stream.
|
144
191
|
class CollectionStream < BasicStream
|
145
|
-
|
192
|
+
attr_reader :pos
|
146
193
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
194
|
+
# Creates a new CollectionStream for the indexable sequence _seq_.
|
195
|
+
def initialize(seq)
|
196
|
+
@seq = seq
|
197
|
+
set_to_begin
|
198
|
+
end
|
152
199
|
|
153
|
-
|
154
|
-
|
200
|
+
def at_end?
|
201
|
+
@pos + 1 >= @seq.size
|
202
|
+
end
|
155
203
|
|
156
|
-
|
204
|
+
def at_beginning?
|
205
|
+
@pos < 0
|
206
|
+
end
|
157
207
|
|
158
|
-
|
159
|
-
def set_to_begin; @pos = -1; end
|
160
|
-
def set_to_end; @pos = @seq.size - 1; end
|
208
|
+
# positioning
|
161
209
|
|
162
|
-
|
163
|
-
|
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
|
164
222
|
|
165
|
-
|
223
|
+
def basic_backward
|
224
|
+
r = @seq[@pos]
|
225
|
+
@pos -= 1; r
|
226
|
+
end
|
227
|
+
|
228
|
+
protected
|
229
|
+
|
230
|
+
# basic_current and basic_peek can be implemented more efficiently than in
|
231
|
+
# superclass
|
232
|
+
def basic_current
|
233
|
+
@seq[@pos]
|
234
|
+
end
|
166
235
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
236
|
+
def basic_peek
|
237
|
+
@seq[@pos + 1]
|
238
|
+
end
|
239
|
+
end
|
171
240
|
|
172
|
-
|
241
|
+
# CollectionStream
|
173
242
|
|
174
243
|
# A simple Iterator for iterating over a sequence of integers starting from
|
175
244
|
# zero up to a given upper bound. Mainly used by Stream::FilteredStream. Could
|
@@ -179,26 +248,44 @@ module Stream
|
|
179
248
|
# The upper bound is stored in the instance variable @stop which can be
|
180
249
|
# incremented dynamically by the method increment_stop.
|
181
250
|
class IntervalStream < BasicStream
|
182
|
-
|
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
|
183
267
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
@stop = stop - 1
|
188
|
-
set_to_begin
|
189
|
-
end
|
268
|
+
def set_to_end
|
269
|
+
@pos = @stop
|
270
|
+
end
|
190
271
|
|
191
|
-
|
192
|
-
|
272
|
+
def set_to_begin
|
273
|
+
@pos = -1
|
274
|
+
end
|
193
275
|
|
194
|
-
|
195
|
-
|
276
|
+
# Increment the upper bound by incr.
|
277
|
+
def increment_stop(incr = 1)
|
278
|
+
@stop += incr
|
279
|
+
end
|
196
280
|
|
197
|
-
|
198
|
-
|
281
|
+
def basic_forward
|
282
|
+
@pos += 1
|
283
|
+
end
|
199
284
|
|
200
|
-
|
201
|
-
|
285
|
+
def basic_backward
|
286
|
+
@pos -= 1
|
287
|
+
@pos + 1
|
288
|
+
end
|
202
289
|
end
|
203
290
|
|
204
291
|
# Class WrappedStream is the abstract superclass for stream classes that wrap
|
@@ -211,25 +298,41 @@ module Stream
|
|
211
298
|
# arrayStream.to_a => [1,2,3]
|
212
299
|
# Stream::WrappedStream.new(arrayStream).to_a => [1,2,3]
|
213
300
|
class WrappedStream < BasicStream
|
214
|
-
|
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
|
215
311
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
end
|
312
|
+
def at_end?
|
313
|
+
@wrapped_stream.at_end?
|
314
|
+
end
|
220
315
|
|
221
|
-
|
222
|
-
|
316
|
+
def set_to_end
|
317
|
+
@wrapped_stream.set_to_end
|
318
|
+
end
|
223
319
|
|
224
|
-
|
225
|
-
|
320
|
+
def set_to_begin
|
321
|
+
@wrapped_stream.set_to_begin
|
322
|
+
end
|
226
323
|
|
227
|
-
|
228
|
-
|
324
|
+
# Returns the wrapped stream unwrapped.
|
325
|
+
def unwrapped
|
326
|
+
@wrapped_stream.unwrapped
|
327
|
+
end
|
229
328
|
|
230
|
-
|
231
|
-
|
232
|
-
|
329
|
+
def basic_forward
|
330
|
+
@wrapped_stream.basic_forward
|
331
|
+
end
|
332
|
+
|
333
|
+
def basic_backward
|
334
|
+
@wrapped_stream.basic_backward
|
335
|
+
end
|
233
336
|
end
|
234
337
|
|
235
338
|
##
|
@@ -240,64 +343,69 @@ module Stream
|
|
240
343
|
#
|
241
344
|
# (1..6).create_stream.filtered { |x| x % 2 == 0 }.to_a ==> [2, 4, 6]
|
242
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
|
243
354
|
|
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
|
-
|
296
|
-
|
297
|
-
|
298
|
-
# Returns the current position of the stream.
|
299
|
-
def pos; @positionHolder.pos; end
|
300
|
-
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
|
301
409
|
|
302
410
|
##
|
303
411
|
# Each reversable stream (a stream that implements #backward and
|
@@ -307,29 +415,43 @@ module Stream
|
|
307
415
|
#
|
308
416
|
# (1..6).create_stream.reverse.to_a ==> [6, 5, 4, 3, 2, 1]
|
309
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
|
310
425
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
super otherStream
|
316
|
-
set_to_begin
|
317
|
-
end
|
426
|
+
# Returns true if the wrapped stream is at_end?.
|
427
|
+
def at_beginning?
|
428
|
+
wrapped_stream.at_end?
|
429
|
+
end
|
318
430
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
431
|
+
# Returns true if the wrapped stream is at_beginning?.
|
432
|
+
def at_end?
|
433
|
+
wrapped_stream.at_beginning?
|
434
|
+
end
|
323
435
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
436
|
+
# Moves the wrapped stream one step backward.
|
437
|
+
def basic_forward
|
438
|
+
wrapped_stream.basic_backward
|
439
|
+
end
|
328
440
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
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
|
450
|
+
|
451
|
+
# Sets the wrapped stream to the end.
|
452
|
+
def set_to_begin
|
453
|
+
wrapped_stream.set_to_end
|
454
|
+
end
|
333
455
|
end
|
334
456
|
|
335
457
|
##
|
@@ -341,21 +463,25 @@ module Stream
|
|
341
463
|
# (1..5).collect {|x| x**2} ==> [1, 4, 9, 16, 25]
|
342
464
|
# (1..5).create_stream.collect {|x| x**2}.to_a ==> [1, 4, 9, 16, 25]
|
343
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
|
344
473
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
@mapping = mapping
|
351
|
-
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
|
352
479
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
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
|
359
485
|
end
|
360
486
|
|
361
487
|
##
|
@@ -366,81 +492,98 @@ module Stream
|
|
366
492
|
#
|
367
493
|
# ((1..3).create_stream + [4,5].create_stream).to_a ==> [1, 2, 3, 4, 5]
|
368
494
|
class ConcatenatedStream < WrappedStream
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
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?
|
383
509
|
return false
|
384
|
-
else
|
385
|
-
until streamOfStreams.at_end?
|
386
|
-
dir, @dirOfLastMove = @dirOfLastMove, :forward
|
387
|
-
s = streamOfStreams.basic_forward
|
388
|
-
# if last move was backwards, then @currentStream is
|
389
|
-
# equivalent to s. Move to next stream.
|
390
|
-
next if dir == :backward
|
391
|
-
s.set_to_begin
|
392
|
-
if s.at_end? # empty stream?
|
393
|
-
next # skip it
|
394
|
-
else
|
395
|
-
@currentStream = s
|
396
|
-
return false # found non empty stream
|
397
|
-
end
|
398
|
-
end # until
|
399
|
-
reachedBoundary # sets @dirOfLastMove and @currentStream
|
400
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
|
401
529
|
end
|
402
530
|
|
403
|
-
|
531
|
+
# Same as at_end? the other way round.
|
532
|
+
# @return [Boolean]
|
404
533
|
def at_beginning?
|
405
|
-
|
406
|
-
|
534
|
+
# same algorithm as at_end? the other way round.
|
535
|
+
unless @current_stream.at_beginning?
|
407
536
|
return false
|
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
|
-
|
442
|
-
|
443
|
-
|
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
|
444
587
|
|
445
588
|
# An ImplicitStream is an easy way to create a stream on the fly without
|
446
589
|
# defining a subclass of BasicStream. The basic methods required for a stream
|
@@ -463,72 +606,96 @@ module Stream
|
|
463
606
|
# remove the first or last element of an existing stream (see remove_first
|
464
607
|
# and remove_last).
|
465
608
|
class ImplicitStream < BasicStream
|
466
|
-
|
609
|
+
attr_writer :at_beginning_proc, :at_end_proc, :forward_proc,
|
467
610
|
:backward_proc, :set_to_begin_proc, :set_to_end_proc
|
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
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
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
|
513
672
|
|
514
673
|
# Stream creation functions
|
515
674
|
|
516
675
|
##
|
517
676
|
# Return a Stream::FilteredStream which iterates over all my elements
|
518
677
|
# satisfying the condition specified by the block.
|
519
|
-
def filtered(&block)
|
678
|
+
def filtered(&block)
|
679
|
+
FilteredStream.new(self, &block)
|
680
|
+
end
|
520
681
|
|
521
682
|
# Create a Stream::ReversedStream wrapper on self.
|
522
|
-
def reverse
|
683
|
+
def reverse
|
684
|
+
ReversedStream.new self
|
685
|
+
end
|
523
686
|
|
524
687
|
# Create a Stream::MappedStream wrapper on self. Instead of returning the
|
525
688
|
# stream element on each move, the value of calling _mapping_ is returned
|
526
689
|
# instead. See Stream::MappedStream for examples.
|
527
|
-
def collect(&mapping)
|
690
|
+
def collect(&mapping)
|
691
|
+
MappedStream.new(self, &mapping)
|
692
|
+
end
|
528
693
|
|
529
694
|
# Create a Stream::ConcatenatedStream on self, which must be a stream of
|
530
695
|
# streams.
|
531
|
-
def concatenate
|
696
|
+
def concatenate
|
697
|
+
ConcatenatedStream.new self
|
698
|
+
end
|
532
699
|
|
533
700
|
# Create a Stream::ConcatenatedStream, concatenated from streams build with
|
534
701
|
# the block for each element of self:
|
@@ -537,33 +704,37 @@ module Stream
|
|
537
704
|
# [i,-i].create_stream
|
538
705
|
# }.
|
539
706
|
# s.to_a ==> [1, -1, 2, -2, 3, -3]
|
540
|
-
def concatenate_collected(&mapping)
|
707
|
+
def concatenate_collected(&mapping)
|
708
|
+
collect(&mapping).concatenate
|
709
|
+
end
|
541
710
|
|
542
711
|
# Create a Stream::ConcatenatedStream by concatenatating the receiver and
|
543
|
-
#
|
712
|
+
# _other_stream_
|
544
713
|
#
|
545
714
|
# (%w(a b c).create_stream + [4,5].create_stream).to_a
|
546
715
|
# ==> ["a", "b", "c", 4, 5]
|
547
|
-
def +(
|
548
|
-
|
716
|
+
def +(other)
|
717
|
+
[self, other].create_stream.concatenate
|
549
718
|
end
|
550
719
|
|
551
720
|
# Create a Stream::ImplicitStream which wraps the receiver stream by modifying
|
552
721
|
# one or more basic methods of the receiver. As an example the method
|
553
722
|
# remove_first uses #modify to create an ImplicitStream which filters the
|
554
723
|
# first element away.
|
555
|
-
def modify(&block)
|
724
|
+
def modify(&block)
|
725
|
+
ImplicitStream.new(self, &block)
|
726
|
+
end
|
556
727
|
|
557
728
|
# Returns a Stream::ImplicitStream wrapping a Stream::FilteredStream, which
|
558
729
|
# eliminates the first element of the receiver.
|
559
730
|
#
|
560
731
|
# (1..3).create_stream.remove_first.to_a ==> [2,3]
|
561
732
|
def remove_first
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
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
|
567
738
|
end
|
568
739
|
|
569
740
|
# Returns a Stream which eliminates the first element of the receiver.
|
@@ -573,7 +744,7 @@ module Stream
|
|
573
744
|
# <em>Take a look at the source. The implementation is inefficient but
|
574
745
|
# elegant.</em>
|
575
746
|
def remove_last
|
576
|
-
|
747
|
+
reverse.remove_first.reverse # I like this one
|
577
748
|
end
|
578
749
|
end
|
579
750
|
|