stream 0.5.2 → 0.5.3
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 +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
|
|