stream 0.5.4 → 0.5.6
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/CHANGELOG.md +8 -0
- data/Gemfile.lock +5 -11
- data/LICENSE +4 -0
- data/README.md +209 -0
- data/Rakefile +0 -32
- data/lib/stream/version.rb +5 -0
- data/lib/stream.rb +25 -7
- data/test/test_helper.rb +0 -1
- metadata +9 -55
- data/README.rdoc +0 -172
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1a1851af382462908d89f216b7b68c4e12a471bf66c8b95f68a8770bce964d5c
|
|
4
|
+
data.tar.gz: cae0261438776cd1b62c70686ec6a9983b45ecabc2476b37d88b9c6a43868272
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ae065f33f579bde4192dfb75f4fc6dd57f20f40708c1614eb236364e4cf61b5848e11f2fa00e29c54f6b63af37135d8c5dd3782336d02c43b50d18742b29d121
|
|
7
|
+
data.tar.gz: 673f7510857e58e5f31b07edf304cce64a1f999f5433ba7c4305dd0ae4eb27aeb01135ab5be314155b22053a80915e91bfc3451e3860a74da710137bbfedc4ef
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.5.6](https://github.com/monora/stream/compare/v0.5.5...v0.5.6) (2026-05-10)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* convert README to Markdown, add copyright, fix badges, clean up ([#16](https://github.com/monora/stream/issues/16)) ([9716019](https://github.com/monora/stream/commit/97160196497cb568ae2558b03d6ac4fc0e267a65))
|
data/Gemfile.lock
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
stream (0.5.
|
|
5
|
-
generator
|
|
4
|
+
stream (0.5.6)
|
|
6
5
|
|
|
7
6
|
GEM
|
|
8
7
|
remote: https://rubygems.org/
|
|
9
8
|
specs:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
rdoc (6.1.1)
|
|
14
|
-
test-unit (3.2.9)
|
|
9
|
+
power_assert (3.0.1)
|
|
10
|
+
rake (13.4.2)
|
|
11
|
+
test-unit (3.7.7)
|
|
15
12
|
power_assert
|
|
16
|
-
yard (0.9.16)
|
|
17
13
|
|
|
18
14
|
PLATFORMS
|
|
19
15
|
ruby
|
|
@@ -21,10 +17,8 @@ PLATFORMS
|
|
|
21
17
|
|
|
22
18
|
DEPENDENCIES
|
|
23
19
|
rake
|
|
24
|
-
rdoc
|
|
25
20
|
stream!
|
|
26
21
|
test-unit
|
|
27
|
-
yard
|
|
28
22
|
|
|
29
23
|
BUNDLED WITH
|
|
30
|
-
2.
|
|
24
|
+
2.5.22
|
data/LICENSE
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2002-2026 Horst Duchene <monora@gmail.com>
|
|
2
|
+
|
|
3
|
+
stream is distributed under the same license as Ruby.
|
|
4
|
+
|
|
1
5
|
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
|
|
2
6
|
You can redistribute it and/or modify it under either the terms of the
|
|
3
7
|
2-clause BSDL (see the file BSDL), or the conditions below:
|
data/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
[](https://github.com/monora/stream/actions/workflows/ruby.yml)
|
|
2
|
+
[](https://rubygems.org/gems/stream)
|
|
3
|
+
|
|
4
|
+
# Extended External Iterators (forward and backward)
|
|
5
|
+
|
|
6
|
+
## Description
|
|
7
|
+
|
|
8
|
+
Module `Stream` defines an interface for [external
|
|
9
|
+
iterators](https://wiki.c2.com/?ExternalIterator). A stream can be seen
|
|
10
|
+
as an iterator on a sequence of objects `x1,...,xn`. The state of the
|
|
11
|
+
stream is uniquely determined by the following methods:
|
|
12
|
+
|
|
13
|
+
* `at_beginning?`
|
|
14
|
+
* `at_end?`
|
|
15
|
+
* `current`
|
|
16
|
+
* `peek`
|
|
17
|
+
|
|
18
|
+
State changes are done with the following operations:
|
|
19
|
+
|
|
20
|
+
* `set_to_begin`
|
|
21
|
+
* `set_to_end`
|
|
22
|
+
* `forward`
|
|
23
|
+
* `backward`
|
|
24
|
+
|
|
25
|
+
With the help of the method `current_edge` the state of a stream `s` can be
|
|
26
|
+
exactly defined
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
s.current_edge == [s.current, s.peek]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If `s` a stream on `[x1,...,xn]`. Consider the edges `[xi,xi+1]` i=1,...,n
|
|
33
|
+
and `[x0,x1]` and `[xn,xn+1]` (x0 and xn+1 are helper elements to define
|
|
34
|
+
the boundary conditions). Then if `s` is non empty, the following
|
|
35
|
+
conditions must be true:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
s.at_beginning? <=> s.current_edge == [x0,x1]
|
|
39
|
+
s.at_end? <=> s.current_edge == [xn,xn+1]
|
|
40
|
+
s.empty? <=> s.at_beginning? && s.at_end? <=> s.current_edge == [x0,x1] <=> n = 0
|
|
41
|
+
s.set_to_end => s.at_end?
|
|
42
|
+
s.set_to_begin => s.at_beginning?
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If `0 <= i < n` and `s.current_edge == [xi, xi+1]`, then:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
[s.forward, s.current_edge] == [xi+1, [xi+1, xi+2]]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If `1 <= i < n` and `s.current_edge == [xi, xi+1]`, then:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
[s.backward, s.current_edge] == [xi, [xi-1, xi]]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The result of `peek` is the same as of `forward` without changing state. The result of
|
|
58
|
+
`current` is the same as of `backward` without changing state.
|
|
59
|
+
|
|
60
|
+
Module `Stream` includes `Enumerable` implementing `each` in the obvious way.
|
|
61
|
+
|
|
62
|
+
Not every stream needs to implement `backward` and `at_beginning?`
|
|
63
|
+
thus being not reversable. If they are reversable `peek` can easily be
|
|
64
|
+
implemented using `forward` and `backward`, as is done in module
|
|
65
|
+
`Stream`. If a stream is not reversable all derived streams provided
|
|
66
|
+
by the stream module (filter, mapping, concatenation) can be used
|
|
67
|
+
anyway. Explicit or implicit (via `peek` or `current`) uses of `backward`
|
|
68
|
+
would throw a `NotImplementedError`.
|
|
69
|
+
|
|
70
|
+
Classes implementing the stream interface must implement the following
|
|
71
|
+
methods:
|
|
72
|
+
|
|
73
|
+
* `basic_forward`
|
|
74
|
+
* `basic_backward`
|
|
75
|
+
* `at_end?`
|
|
76
|
+
* `at_beginning?`
|
|
77
|
+
|
|
78
|
+
The methods `set_to_end` and `set_to_begin` are by default implemented as:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
set_to_end : until at_end?; do basic_forward end
|
|
82
|
+
set_to_begin : until at_beginning?; do basic_backward end
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The methods `forward` and `backward` are by default implemented as:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
forward: raise EndOfStreamException if at_end?; basic_forward.
|
|
89
|
+
backward: raise EndOfStreamException if at_beginning?; basic_backward
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Thus subclasses must only implement **four** methods. Efficiency sometimes
|
|
93
|
+
demands better implementations.
|
|
94
|
+
|
|
95
|
+
There are several concrete classes implementing the stream interface:
|
|
96
|
+
|
|
97
|
+
* `Stream::EmptyStream` (boring)
|
|
98
|
+
* `Stream::CollectionStream` created by the method `Array#create_stream`
|
|
99
|
+
* `Stream::FilteredStream` created by the method `Stream#filtered`
|
|
100
|
+
* `Stream::ReversedStream` created by the method `Stream#reverse`
|
|
101
|
+
* `Stream::ConcatenatedStream` created by the method `Stream#concatenate`
|
|
102
|
+
* `Stream::ImplicitStream` using closures for the basic methods to implement
|
|
103
|
+
|
|
104
|
+
## Installation
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
gem install stream
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
or download the latest sources from the git repository
|
|
111
|
+
<https://github.com/monora/stream>.
|
|
112
|
+
|
|
113
|
+
## Examples
|
|
114
|
+
|
|
115
|
+
### Iterate over three streams
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
g = ('a'..'f').create_stream
|
|
119
|
+
h = (1..10).create_stream
|
|
120
|
+
i = (10..20).create_stream
|
|
121
|
+
|
|
122
|
+
until g.at_end? || h.at_end? || i.at_end?
|
|
123
|
+
p [g.forward, h.forward, i.forward]
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Output:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
["a", 1, 10]
|
|
131
|
+
["b", 2, 11]
|
|
132
|
+
["c", 3, 12]
|
|
133
|
+
["d", 4, 13]
|
|
134
|
+
["e", 5, 14]
|
|
135
|
+
["f", 6, 15]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Concatenate file streams
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
def filestream fname
|
|
142
|
+
Stream::ImplicitStream.new { |s|
|
|
143
|
+
f = open(fname)
|
|
144
|
+
s.at_end_proc = proc {f.eof?}
|
|
145
|
+
s.forward_proc = proc {f.readline}
|
|
146
|
+
# Need not implement backward moving to use the framework
|
|
147
|
+
}
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
(filestream("/etc/passwd") + ('a'..'f').create_stream + filestream("/etc/group")).each do |l|
|
|
151
|
+
puts l
|
|
152
|
+
end
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Two filtered collection streams concatenated and reversed
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
def newstream; (1..6).create_stream; end
|
|
159
|
+
s = newstream.filtered { |x| x % 2 == 0 } + newstream.filtered { |x| x % 2 != 0 }
|
|
160
|
+
s = s.reverse
|
|
161
|
+
puts "Contents : #{s.to_a.join ' '}"
|
|
162
|
+
puts "At end? : #{s.at_end?}"
|
|
163
|
+
puts "At beginning? : #{s.at_beginning?}"
|
|
164
|
+
puts "2xBackwards : #{s.backward} #{s.backward}"
|
|
165
|
+
puts "Forward : #{s.forward}"
|
|
166
|
+
puts "Peek : #{s.peek}"
|
|
167
|
+
puts "Current : #{s.current}"
|
|
168
|
+
puts "set_to_begin : Peek=#{s.set_to_begin;s.peek}"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Output:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
Contents : 5 3 1 6 4 2
|
|
175
|
+
At end? : true
|
|
176
|
+
At beginning? : false
|
|
177
|
+
2xBackwards : 2 4
|
|
178
|
+
Forward : 4
|
|
179
|
+
Peek : 2
|
|
180
|
+
Current : 4
|
|
181
|
+
set_to_begin : Peek=5
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### An infinite stream (do not use `set_to_end`!)
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
def randomStream
|
|
188
|
+
Stream::ImplicitStream.new { |s|
|
|
189
|
+
s.set_to_begin_proc = proc {srand 1234}
|
|
190
|
+
s.at_end_proc = proc {false}
|
|
191
|
+
s.forward_proc = proc {rand}
|
|
192
|
+
}
|
|
193
|
+
end
|
|
194
|
+
s = randomStream.filtered { |x| x >= 0.5 }.collect { |x| sprintf("%5.2f",x*100) }
|
|
195
|
+
puts "5 random numbers: #{(1..5).collect {|x| s.forward}}\n"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Output:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
5 random numbers: ["62.21", "78.54", "78.00", "80.19", "95.81"]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Copying
|
|
205
|
+
|
|
206
|
+
stream is Copyright (c) 2002-2026 by Horst Duchene.
|
|
207
|
+
|
|
208
|
+
It is free software, and may be redistributed under the terms specified
|
|
209
|
+
in the [LICENSE](LICENSE) file.
|
data/Rakefile
CHANGED
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
# Rakefile for stream -*- ruby -*-
|
|
2
2
|
|
|
3
|
-
require 'rubygems'
|
|
4
3
|
require 'bundler/setup'
|
|
5
|
-
require 'rubygems/package_task'
|
|
6
4
|
|
|
7
5
|
require 'rake/testtask'
|
|
8
|
-
require 'rake/clean'
|
|
9
|
-
require 'yard'
|
|
10
|
-
|
|
11
|
-
$:.unshift File.join(File.dirname(__FILE__), 'lib')
|
|
12
|
-
require 'stream' # require module to STREAM_VERSION
|
|
13
|
-
|
|
14
|
-
SRC_RB = FileList['lib/*.rb']
|
|
15
|
-
|
|
16
|
-
# The default task is run if rake is given no explicit arguments.
|
|
17
6
|
|
|
18
7
|
desc "Default Task"
|
|
19
8
|
task :default => :test
|
|
20
9
|
|
|
21
|
-
# Define a test task.
|
|
22
|
-
|
|
23
10
|
Rake::TestTask.new do |t|
|
|
24
11
|
t.libs << 'test'
|
|
25
12
|
t.pattern = 'test/test*.rb'
|
|
@@ -28,23 +15,4 @@ end
|
|
|
28
15
|
|
|
29
16
|
task :test
|
|
30
17
|
|
|
31
|
-
# Define a test that will run all the test targets.
|
|
32
|
-
desc "Run all test targets"
|
|
33
|
-
task :testall => [:test ]
|
|
34
|
-
|
|
35
|
-
# Git tagging
|
|
36
|
-
|
|
37
|
-
desc "Commit all changes as a new version commit. Tag the commit with v<version> tag"
|
|
38
|
-
task :tag do
|
|
39
|
-
puts "Committing and tagging version #{STREAM_VERSION}"
|
|
40
|
-
`git commit -am 'Version #{STREAM_VERSION}'`
|
|
41
|
-
`git tag 'v#{STREAM_VERSION}'`
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Documentation
|
|
45
|
-
|
|
46
|
-
YARD::Rake::YardocTask.new
|
|
47
|
-
|
|
48
|
-
# Tasks for building and installing Stream gem.
|
|
49
|
-
|
|
50
18
|
Bundler::GemHelper.install_tasks
|
data/lib/stream.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'stream/version'
|
|
2
4
|
|
|
3
5
|
##
|
|
4
6
|
# Module Stream defines an interface for an external Iterator which
|
|
@@ -24,15 +26,17 @@ module Stream
|
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
# Move forward one position. Returns the _target_ of current_edge.
|
|
27
|
-
#
|
|
29
|
+
# @raise [EndOfStreamException] if at_end? is true
|
|
30
|
+
# @return [Object] the next element
|
|
28
31
|
def forward
|
|
29
32
|
raise EndOfStreamException if at_end?
|
|
30
33
|
|
|
31
34
|
basic_forward
|
|
32
35
|
end
|
|
33
36
|
|
|
34
|
-
# Move backward one position. Returns the _source_ of current_edge.
|
|
35
|
-
#
|
|
37
|
+
# Move backward one position. Returns the _source_ of current_edge.
|
|
38
|
+
# @raise [EndOfStreamException] if at_beginning? is true
|
|
39
|
+
# @return [Object] the previous element
|
|
36
40
|
def backward
|
|
37
41
|
raise EndOfStreamException if at_beginning?
|
|
38
42
|
|
|
@@ -79,6 +83,8 @@ module Stream
|
|
|
79
83
|
# This is similar to #detect, but starts the search from the
|
|
80
84
|
# current position. #detect, which is inherited from Enumerable uses
|
|
81
85
|
# #each, which implicitly calls #set_to_begin.
|
|
86
|
+
# @yield [element] each element in forward direction
|
|
87
|
+
# @return [Object, nil] the first matching element, or nil
|
|
82
88
|
def move_forward_until
|
|
83
89
|
until at_end?
|
|
84
90
|
element = basic_forward
|
|
@@ -89,6 +95,8 @@ module Stream
|
|
|
89
95
|
|
|
90
96
|
# Move backward until the boolean block is not false and returns the element
|
|
91
97
|
# found. Returns nil if no object matches.
|
|
98
|
+
# @yield [element] each element in backward direction
|
|
99
|
+
# @return [Object, nil] the first matching element, or nil
|
|
92
100
|
def move_backward_until
|
|
93
101
|
until at_beginning?
|
|
94
102
|
element = basic_backward
|
|
@@ -192,6 +200,7 @@ module Stream
|
|
|
192
200
|
attr_reader :pos
|
|
193
201
|
|
|
194
202
|
# Creates a new CollectionStream for the indexable sequence _seq_.
|
|
203
|
+
# @param seq [Array] an integer-indexed collection
|
|
195
204
|
def initialize(seq)
|
|
196
205
|
@seq = seq
|
|
197
206
|
set_to_begin
|
|
@@ -252,6 +261,7 @@ module Stream
|
|
|
252
261
|
|
|
253
262
|
# Create a new IntervalStream with upper bound _stop_. stop - 1 is the last
|
|
254
263
|
# element. By default _stop_ is zero which means that the stream is empty.
|
|
264
|
+
# @param stop [Integer] exclusive upper bound; stream yields 0..stop-1
|
|
255
265
|
def initialize(stop = 0)
|
|
256
266
|
@stop = stop - 1
|
|
257
267
|
set_to_begin
|
|
@@ -617,6 +627,8 @@ module Stream
|
|
|
617
627
|
#
|
|
618
628
|
# If a block is given to new, than it is called with the new ImplicitStream
|
|
619
629
|
# stream as parameter letting the client overwriting the default blocks.
|
|
630
|
+
# @param other_stream [Stream, nil] optional stream to wrap
|
|
631
|
+
# @yield [self] the new ImplicitStream instance for customization
|
|
620
632
|
def initialize(other_stream = nil)
|
|
621
633
|
# Initialize with defaults
|
|
622
634
|
@at_beginning_proc = proc { true }
|
|
@@ -674,7 +686,9 @@ module Stream
|
|
|
674
686
|
|
|
675
687
|
##
|
|
676
688
|
# Return a Stream::FilteredStream which iterates over all my elements
|
|
677
|
-
# satisfying the condition specified
|
|
689
|
+
# satisfying the condition specified by the block.
|
|
690
|
+
# @yield [element] filter predicate
|
|
691
|
+
# @return [FilteredStream]
|
|
678
692
|
def filtered(&block)
|
|
679
693
|
FilteredStream.new(self, &block)
|
|
680
694
|
end
|
|
@@ -687,6 +701,8 @@ module Stream
|
|
|
687
701
|
# Create a Stream::MappedStream wrapper on self. Instead of returning the
|
|
688
702
|
# stream element on each move, the value of calling _mapping_ is returned
|
|
689
703
|
# instead. See Stream::MappedStream for examples.
|
|
704
|
+
# @yield [element] mapping block applied to each element
|
|
705
|
+
# @return [MappedStream]
|
|
690
706
|
def collect(&mapping)
|
|
691
707
|
MappedStream.new(self, &mapping)
|
|
692
708
|
end
|
|
@@ -708,8 +724,10 @@ module Stream
|
|
|
708
724
|
collect(&mapping).concatenate
|
|
709
725
|
end
|
|
710
726
|
|
|
711
|
-
# Create a Stream::ConcatenatedStream by
|
|
712
|
-
# _other_stream_
|
|
727
|
+
# Create a Stream::ConcatenatedStream by concatenating the receiver and
|
|
728
|
+
# _other_stream_.
|
|
729
|
+
# @param other [Stream] the stream to append
|
|
730
|
+
# @return [ConcatenatedStream]
|
|
713
731
|
#
|
|
714
732
|
# (%w(a b c).create_stream + [4,5].create_stream).to_a
|
|
715
733
|
# ==> ["a", "b", "c", 4, 5]
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: stream
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Horst Duchene
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-05-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: generator
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - ">="
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - ">="
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0'
|
|
27
13
|
- !ruby/object:Gem::Dependency
|
|
28
14
|
name: rake
|
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -38,34 +24,6 @@ dependencies:
|
|
|
38
24
|
- - ">="
|
|
39
25
|
- !ruby/object:Gem::Version
|
|
40
26
|
version: '0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: yard
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: rdoc
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
69
27
|
- !ruby/object:Gem::Dependency
|
|
70
28
|
name: test-unit
|
|
71
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -84,17 +42,18 @@ description: Module Stream defines an interface for external iterators.
|
|
|
84
42
|
email: monora@gmail.com
|
|
85
43
|
executables: []
|
|
86
44
|
extensions: []
|
|
87
|
-
extra_rdoc_files:
|
|
88
|
-
- README.rdoc
|
|
45
|
+
extra_rdoc_files: []
|
|
89
46
|
files:
|
|
47
|
+
- CHANGELOG.md
|
|
90
48
|
- Gemfile
|
|
91
49
|
- Gemfile.lock
|
|
92
50
|
- LICENSE
|
|
93
|
-
- README.
|
|
51
|
+
- README.md
|
|
94
52
|
- Rakefile
|
|
95
53
|
- examples/examples.rb
|
|
96
54
|
- examples/streamtester.rb
|
|
97
55
|
- lib/stream.rb
|
|
56
|
+
- lib/stream/version.rb
|
|
98
57
|
- test/test_helper.rb
|
|
99
58
|
- test/teststream.rb
|
|
100
59
|
homepage: https://github.com/monora/stream
|
|
@@ -102,12 +61,7 @@ licenses:
|
|
|
102
61
|
- ruby
|
|
103
62
|
metadata: {}
|
|
104
63
|
post_install_message:
|
|
105
|
-
rdoc_options:
|
|
106
|
-
- "--title"
|
|
107
|
-
- stream - Extended External Iterators
|
|
108
|
-
- "--main"
|
|
109
|
-
- README.rdoc
|
|
110
|
-
- "--line-numbers"
|
|
64
|
+
rdoc_options: []
|
|
111
65
|
require_paths:
|
|
112
66
|
- lib
|
|
113
67
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
@@ -121,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
121
75
|
- !ruby/object:Gem::Version
|
|
122
76
|
version: '0'
|
|
123
77
|
requirements: []
|
|
124
|
-
rubygems_version: 3.
|
|
78
|
+
rubygems_version: 3.5.22
|
|
125
79
|
signing_key:
|
|
126
80
|
specification_version: 4
|
|
127
81
|
summary: stream - Extended External Iterators
|
data/README.rdoc
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
{<img src="https://github.com/monora/stream/actions/workflows/ruby.yml/badge.svg" alt="Build Status" />}[https://github.com/monora/stream/actions/workflows/ruby.yml]
|
|
2
|
-
|
|
3
|
-
= Extended External Iterators (forward and backward)
|
|
4
|
-
|
|
5
|
-
== Description
|
|
6
|
-
|
|
7
|
-
Module Stream defines an interface for external iterators. A stream can be
|
|
8
|
-
seen as an iterator on a sequence of objects x1,...,xn. The state of the
|
|
9
|
-
stream is uniquely determined by the following methods:
|
|
10
|
-
|
|
11
|
-
* at_beginning?
|
|
12
|
-
* at_end?
|
|
13
|
-
* current
|
|
14
|
-
* peek
|
|
15
|
-
|
|
16
|
-
State changes are done with the following operations:
|
|
17
|
-
|
|
18
|
-
* set_to_begin
|
|
19
|
-
* set_to_end
|
|
20
|
-
* forward
|
|
21
|
-
* backward
|
|
22
|
-
|
|
23
|
-
With the help of the method current_edge the state of a stream s can be
|
|
24
|
-
exactly defined
|
|
25
|
-
|
|
26
|
-
s.current_edge == [s.current, s.peek]
|
|
27
|
-
|
|
28
|
-
If s a stream on [x1,...,xn]. Consider the edges [xi,xi+1] i=1,...,n and
|
|
29
|
-
[x0,x1] and [xn,xn+1] (x0 and xn+1 are helper elements to define the boundary
|
|
30
|
-
conditions). Then if s is non empty, the following conditions must be true:
|
|
31
|
-
|
|
32
|
-
s.at_beginning? <=> s.current_edge == [x0,x1]
|
|
33
|
-
s.at_end? <=> s.current_edge == [xn,xn+1]
|
|
34
|
-
s.isEmpty? <=> s.at_beginning? && s.at_end? <=> s.current_edge == [x0,x1] <=> n = 0
|
|
35
|
-
s.set_to_end => s.at_end?
|
|
36
|
-
s.set_to_begin => s.at_beginning?
|
|
37
|
-
|
|
38
|
-
If 0 <= i < n and s.current_edge == [xi, xi+1] , then:
|
|
39
|
-
|
|
40
|
-
[s.forward, s.current_edge] == [xi+1, [xi+1, xi+2]]
|
|
41
|
-
|
|
42
|
-
If 1 <= i < n and s.current_edge == [xi, xi+1] , then:
|
|
43
|
-
|
|
44
|
-
[s.backward, s.current_edge] == [xi, [xi-1, xi]]
|
|
45
|
-
|
|
46
|
-
The result of peek is the same as of forward without changing state. The result of
|
|
47
|
-
current is the same as of backward without changing state.
|
|
48
|
-
|
|
49
|
-
Module Stream includes Enumerable implementing #each in the obvious way.
|
|
50
|
-
|
|
51
|
-
Not every stream needs to implement #backward and #at_beginning? thus being
|
|
52
|
-
not reversable. If they are reversable peek can easily be implemented using
|
|
53
|
-
forward and backward, as is done in module Stream. If a stream is not
|
|
54
|
-
reversable all derived streams provided by the stream module (filter,
|
|
55
|
-
mapping, concatenation) can be used anyway. Explicit or implicit (via peek or
|
|
56
|
-
current) uses of backward would throw a NotImplementedError.
|
|
57
|
-
|
|
58
|
-
Classes implementing the stream interface must implement the following methods:
|
|
59
|
-
|
|
60
|
-
* basic_forward
|
|
61
|
-
* basic_backward
|
|
62
|
-
|
|
63
|
-
* at_end?
|
|
64
|
-
* at_beginning?
|
|
65
|
-
|
|
66
|
-
The methods set_to_end and set_to_begin are by default implemented as:
|
|
67
|
-
|
|
68
|
-
set_to_end : until at_end?; do basic_forward end
|
|
69
|
-
set_to_begin : until at_beginning?; do basic_backward end
|
|
70
|
-
|
|
71
|
-
The methods forward and backward are by default implemented as:
|
|
72
|
-
|
|
73
|
-
forward: raise EndOfStreamException if at_end?; basic_forward.
|
|
74
|
-
backward: raise EndOfStreamException if at_beginning?; basic_backward
|
|
75
|
-
|
|
76
|
-
Thus subclasses must only implement *four* methods. Efficiency sometimes
|
|
77
|
-
demands better implementations.
|
|
78
|
-
|
|
79
|
-
There are several concrete classes implementing the stream interface:
|
|
80
|
-
|
|
81
|
-
* Stream::EmptyStream (boring)
|
|
82
|
-
* Stream::CollectionStream created by the method Array#create_stream
|
|
83
|
-
* Stream::FilteredStream created by the method Stream#filtered
|
|
84
|
-
* Stream::ReversedStream created by the method Stream#reverse
|
|
85
|
-
* Stream::ConcatenatedStream created by the method Stream#concatenate
|
|
86
|
-
* Stream::ImplicitStream using closures for the basic methods to implement
|
|
87
|
-
|
|
88
|
-
== Download
|
|
89
|
-
|
|
90
|
-
The latest version of stream.rb can be found at
|
|
91
|
-
|
|
92
|
-
* http://github.com/monora/stream
|
|
93
|
-
|
|
94
|
-
== Installation
|
|
95
|
-
|
|
96
|
-
=== Normal Installation
|
|
97
|
-
|
|
98
|
-
You can install stream with the following command.
|
|
99
|
-
|
|
100
|
-
% ruby install.rb
|
|
101
|
-
|
|
102
|
-
from its distribution directory.
|
|
103
|
-
|
|
104
|
-
=== GEM Installation
|
|
105
|
-
|
|
106
|
-
Download the GEM file and install it with ..
|
|
107
|
-
|
|
108
|
-
gem -i stream-VERSION.gem
|
|
109
|
-
|
|
110
|
-
Use the correct version number for VERSION (e.g. 0.5). You may need
|
|
111
|
-
root privileges to install.
|
|
112
|
-
|
|
113
|
-
== See also
|
|
114
|
-
|
|
115
|
-
* Streams in Smalltalk: http://wiki.cs.uiuc.edu/PatternStories/FunWithStreams
|
|
116
|
-
* Simon Strandgaards iterator.rb[http://aeditor.rubyforge.org/iterator/files/iterator_rb.html]
|
|
117
|
-
|
|
118
|
-
== Examples
|
|
119
|
-
|
|
120
|
-
g = ('a'..'f').create_stream
|
|
121
|
-
h = (1..10).create_stream
|
|
122
|
-
i = (10..20).create_stream
|
|
123
|
-
|
|
124
|
-
until g.at_end? || h.at_end? || i.at_end?
|
|
125
|
-
p [g.forward, h.forward, i.forward]
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def filestream fname
|
|
129
|
-
Stream::ImplicitStream.new { |s|
|
|
130
|
-
f = open(fname)
|
|
131
|
-
s.at_end_proc = proc {f.eof?}
|
|
132
|
-
s.forward_proc = proc {f.readline}
|
|
133
|
-
# Need not implement backward moving to use the framework
|
|
134
|
-
}
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
(filestream("/etc/passwd") + ('a'..'f').create_stream + filestream("/etc/group")).each do |l|
|
|
138
|
-
puts l
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
puts "\nTwo filtered collection streams concatenated and reversed:\n\n"
|
|
142
|
-
|
|
143
|
-
def newstream; (1..6).create_stream; end
|
|
144
|
-
s = newstream.filtered { |x| x % 2 == 0 } + newstream.filtered { |x| x % 2 != 0 }
|
|
145
|
-
s = s.reverse
|
|
146
|
-
puts "Contents : #{s.to_a.join ' '}"
|
|
147
|
-
puts "At end? : #{s.at_end?}"
|
|
148
|
-
puts "At beginning? : #{s.at_beginning?}"
|
|
149
|
-
puts "2xBackwards : #{s.backward} #{s.backward}"
|
|
150
|
-
puts "Forward : #{s.forward}"
|
|
151
|
-
puts "Peek : #{s.peek}"
|
|
152
|
-
puts "Current : #{s.current}"
|
|
153
|
-
puts "set_to_begin : Peek=#{s.set_to_begin;s.peek}"
|
|
154
|
-
|
|
155
|
-
# an infinite stream (do not use set_to_end!)
|
|
156
|
-
def randomStream
|
|
157
|
-
Stream::ImplicitStream.new { |s|
|
|
158
|
-
s.set_to_begin_proc = proc {srand 1234}
|
|
159
|
-
s.at_end_proc = proc {false}
|
|
160
|
-
s.forward_proc = proc {rand}
|
|
161
|
-
}
|
|
162
|
-
end
|
|
163
|
-
s = randomStream.filtered { |x| x >= 0.5 }.collect { |x| sprintf("%5.2f ",x*100) }
|
|
164
|
-
puts "5 random numbers: #{(1..5).collect {|x| s.forward}}\n" # =>
|
|
165
|
-
|
|
166
|
-
5 random numbers: 74.05 94.80 87.87 86.07 83.70
|
|
167
|
-
|
|
168
|
-
== Other Stuff
|
|
169
|
-
|
|
170
|
-
Author:: Horst Duchene
|
|
171
|
-
License:: Copyright (c) 2001, 2013, 2016, 2020 Horst Duchene
|
|
172
|
-
Released under the same license as Ruby
|