ruby-stream 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 671bd48c3710f55b50a919a2e5649f04e431e576
4
+ data.tar.gz: f8af1124273e7f59bd118c1b83b7f8a0951cb761
5
+ SHA512:
6
+ metadata.gz: d1f62a98ae959111f0733f0ca186412bf04a2c397bec51445e4e74f3e16ed9e941147bde62577ac52a5c1270f0ce5b9443203f5ea123e2a0266a68382a945143
7
+ data.tar.gz: d3e05556757511d3ce946a714a79b7fe207b0d0a1e5366ecc1a11b9ac32890aaee7d2803deba99704967592f0b0ca6e2b8a43a90a162438cf13f4ec6db736f4f
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ group :test do
4
+ gem 'rake'
5
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,10 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ rake (10.0.3)
5
+
6
+ PLATFORMS
7
+ ruby
8
+
9
+ DEPENDENCIES
10
+ rake
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ #ruby-stream
2
+
3
+ [![Build Status](https://travis-ci.org/oetzi/ruby-stream.png?branch=master)](https://travis-ci.org/oetzi/ruby-stream)
4
+
5
+ Lazy stream implementation for Ruby because Enumerators are kinda silly.
6
+
7
+ ## Description
8
+
9
+ Streams are infinite, lazily evaluated, awesome lists. Here's an example
10
+ of a Stream that represents the set of positive integers:
11
+
12
+ def int_stream(i)
13
+ Stream.new(i) do
14
+ int_stream(i + 1)
15
+ end
16
+ end
17
+
18
+ integers = int_stream(1)
19
+
20
+ You can access streams like any other data structure:
21
+
22
+ integers[0] # => 1
23
+ integers[12] # => 13
24
+ integers[999999] # => 999998
25
+
26
+ Each value will be lazily calculated by materializing the Streams values
27
+ until the requested value. Streams are stateless so a Stream will
28
+ recalculate for every access.
29
+
30
+ You can also create finite Streams from infinite ones:
31
+
32
+ integers.take(5)
33
+
34
+ Finite streams have some extra functionality due to their ability to
35
+ end:
36
+
37
+ integers.take(5).each do |i|
38
+ puts i
39
+ end
40
+
41
+ integers.length # => 5
42
+
43
+ Infinite Streams also support interation and length calculations but you
44
+ will need to be prepared to wait, forever. No seriously, I mean for all
45
+ time. Its infinite.
46
+
47
+ Streams become most powerful when used with high order functions. At the
48
+ moment ruby-stream supports `map` and `filter` operations that operate
49
+ as they would on normal collections (these operations will only actually be
50
+ applied to Stream elements on access or iteration however).
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => :test
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs.push "lib"
7
+ t.test_files = FileList['spec/*_spec.rb']
8
+ t.verbose = true
9
+ end
@@ -0,0 +1,123 @@
1
+ class Stream
2
+ def self.continually(&block)
3
+ Stream.new(yield) do
4
+ Stream.continually(&block)
5
+ end
6
+ end
7
+
8
+ attr_reader :head
9
+
10
+ def initialize(head, &block)
11
+ @head = head
12
+ @tail_block = block
13
+ end
14
+
15
+ def tail
16
+ @tail_block.call
17
+ end
18
+
19
+ def [](n)
20
+ if n == 0
21
+ self.head
22
+ elsif n < 0
23
+ nil
24
+ else
25
+ last_stream = self
26
+ n.times {
27
+ return nil if last_stream.kind_of?(EmptyStream)
28
+ last_stream = last_stream.tail
29
+ }
30
+
31
+ last_stream.head
32
+ end
33
+ end
34
+
35
+ def length
36
+ counter = 0
37
+ self.each { |ele| counter += 1 }
38
+ counter
39
+ end
40
+
41
+ def each(&block)
42
+ last_stream = self
43
+
44
+ until last_stream.kind_of?(EmptyStream)
45
+ block.call(last_stream.head)
46
+ last_stream = last_stream.tail
47
+ end
48
+
49
+ nil
50
+ end
51
+
52
+ def take(n)
53
+ if n <= 0
54
+ EmptyStream.new
55
+ else
56
+ Stream.new(head) do
57
+ tail.take(n - 1)
58
+ end
59
+ end
60
+ end
61
+
62
+ def take_while(&block)
63
+ if block.call(head)
64
+ Stream.new(head) do
65
+ tail.take_while(&block)
66
+ end
67
+ else
68
+ EmptyStream.new
69
+ end
70
+ end
71
+
72
+ def map(&block)
73
+ Stream.new(yield head) do
74
+ tail.map(&block)
75
+ end
76
+ end
77
+
78
+ def filter(&block)
79
+ if block.call(head)
80
+ Stream.new(head) do
81
+ tail.filter(&block)
82
+ end
83
+ else
84
+ tail.filter(&block)
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ class EmptyStream < Stream
91
+ def initialize()
92
+ @head = nil
93
+ end
94
+
95
+ def tail
96
+ EmptyStream.new
97
+ end
98
+
99
+ def [](n)
100
+ nil
101
+ end
102
+
103
+ def each
104
+ nil
105
+ end
106
+
107
+ def take(n)
108
+ EmptyStream.new
109
+ end
110
+
111
+ def take_while(&block)
112
+ EmptyStream.new
113
+ end
114
+
115
+ def map(&block)
116
+ EmptyStream.new
117
+ end
118
+
119
+ def filter(&block)
120
+ EmptyStream.new
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,14 @@
1
+ lib = File.expand_path('../lib/', __FILE__)
2
+ $:.unshift lib unless $:.include?(lib)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "ruby-stream"
6
+ s.version = "0.1.0"
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Callum Stott"]
9
+ s.email = ["callum.stott@me.com"]
10
+ s.summary = "Lazy stream implementation for Ruby"
11
+
12
+ s.require_paths = ['lib']
13
+ s.files = `git ls-files`.split("\n")
14
+ end
@@ -0,0 +1,197 @@
1
+ require 'minitest/autorun'
2
+ require 'ruby-stream'
3
+
4
+ def int_helper(i)
5
+ Stream.new(i) do
6
+ int_helper(i + 1)
7
+ end
8
+ end
9
+
10
+ describe Stream do
11
+ before do
12
+ @stream = int_helper(1)
13
+ end
14
+
15
+ describe "#head" do
16
+ it "returns the first element of the Stream" do
17
+ @stream.head.must_equal 1
18
+ end
19
+ end
20
+
21
+ describe "#tail" do
22
+ it "returns another Stream" do
23
+ @stream.tail.kind_of?(Stream).must_equal true
24
+ end
25
+
26
+ it "returns a Stream with the next element as its head" do
27
+ @stream.tail.head.must_equal 2
28
+ @stream.tail.tail.head.must_equal 3
29
+ end
30
+ end
31
+
32
+ describe "#[](n)" do
33
+ it "calculates the nth element of the stream" do
34
+ @stream[999].must_equal 1000
35
+ end
36
+
37
+ it "returns nil for negative n" do
38
+ @stream[-1].must_equal nil
39
+ end
40
+
41
+ describe "for a finite Stream" do
42
+ it "returns nil for n greater than the limit of the Stream" do
43
+ @stream.take(10)[10].must_equal nil
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "#take(n)" do
49
+ it "returns another Stream" do
50
+ @stream.take(10).kind_of?(Stream).must_equal true
51
+ end
52
+
53
+ it "returns a Stream with the correct #length" do
54
+ @stream.take(10).length.must_equal 10
55
+ end
56
+
57
+ it "returns a Stream with the correct values" do
58
+ stream = @stream.take(10)
59
+ stream[0].must_equal 1
60
+ stream[5].must_equal 6
61
+ stream[7].must_equal 8
62
+ stream[9].must_equal 10
63
+ end
64
+
65
+ it "returns a Stream that can be iterated through finitely" do
66
+ @stream.take(10).each do |element|
67
+ true.must_equal true
68
+ end
69
+ end
70
+
71
+ describe "when operating on the result Stream" do
72
+ it "returns a finite Stream for #map" do
73
+ stream = @stream.take(10).map { |i| i.to_s }
74
+ stream.length.must_equal 10
75
+ end
76
+
77
+ it "returns a finite Stream for #filter" do
78
+ stream = @stream.take(10).filter { |i| true }
79
+ stream.length.must_equal 10
80
+ end
81
+ end
82
+
83
+ describe "for a finite Stream" do
84
+ it "returns a Stream with length limit if n > limit" do
85
+ original = @stream.take(10)
86
+ original.take(100).length.must_equal 10
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "#each(func)" do
92
+ describe "for a finite Stream" do
93
+ it "should return nil" do
94
+ @stream.take(5).each do
95
+ 1
96
+ end.must_equal nil
97
+ end
98
+
99
+ it "should execute the passed block for every element of the stream" do
100
+ i = 0
101
+ @stream.take(5).each do
102
+ i += 1
103
+ end
104
+
105
+ i.must_equal 5
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "#length" do
111
+ it "returns the lenght for a finite array" do
112
+ @stream.take(5).length.must_equal 5
113
+ end
114
+ end
115
+
116
+ describe "#map(func)" do
117
+ it "returns a new Stream" do
118
+ @stream.map(&:to_s).kind_of?(Stream).must_equal true
119
+ end
120
+
121
+ it "returns a new Stream with mapped values" do
122
+ mapped = @stream.map { |i| i + 1 }
123
+ mapped[0].must_equal(2)
124
+ mapped[3].must_equal(5)
125
+ mapped[1000].must_equal(1002)
126
+ end
127
+ end
128
+
129
+ describe "#filter(func)" do
130
+ it "returns a new Stream" do
131
+ @stream.filter { |i| i % 2 == 0 }.kind_of?(Stream).must_equal true
132
+ end
133
+
134
+ it "returns a new Stream with only elements matching the predicate" do
135
+ filtered = @stream.filter { |i| i % 2 == 0 }
136
+ filtered[0].must_equal 2
137
+ filtered[1].must_equal 4
138
+ filtered[3].must_equal 8
139
+ end
140
+ end
141
+
142
+ describe "#take_while(func)" do
143
+ it "returns a new Stream" do
144
+ @stream.take_while { |i| i < 10 }.kind_of?(Stream).must_equal true
145
+ end
146
+
147
+ describe "when the return Stream is finite" do
148
+ it "returns a Stream with the correct length" do
149
+ @stream.take_while { |i| i < 10 }.length.must_equal 9
150
+ end
151
+
152
+ it "returns a Stream that iterates through finitely" do
153
+ stream = @stream.take_while { |i| i < 10 }
154
+ counter = 0
155
+ stream.each { |i| counter += 1 }
156
+ counter.must_equal 9
157
+ end
158
+
159
+ it "returns a finite Stream for #map" do
160
+ stream = @stream.take_while { |i|
161
+ i < 10
162
+ }.map { |i| i.to_s }
163
+
164
+ stream.length.must_equal 9
165
+ end
166
+
167
+ it "returns a finite Stream for #filter" do
168
+ stream = @stream.take_while { |i|
169
+ i < 10
170
+ }.filter { |i| true }
171
+
172
+ stream.length.must_equal 9
173
+ end
174
+ end
175
+ end
176
+
177
+ describe ".continually(func)" do
178
+ it "returns a new Stream" do
179
+ Stream.continually {
180
+ true
181
+ }.kind_of?(Stream).must_equal true
182
+ end
183
+
184
+ it "returns a Stream with the calculated block as each element" do
185
+ stream_block = Proc.new do
186
+ counter = 0
187
+ Stream.continually {
188
+ counter += 1
189
+ }
190
+ end
191
+
192
+ stream_block.call.head.must_equal 1
193
+ stream_block.call.tail.head.must_equal 2
194
+ stream_block.call.tail.tail.head.must_equal 3
195
+ end
196
+ end
197
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-stream
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Callum Stott
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-03-04 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - callum.stott@me.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - Gemfile
21
+ - Gemfile.lock
22
+ - README.md
23
+ - Rakefile
24
+ - lib/ruby-stream.rb
25
+ - ruby-stream.gemspec
26
+ - spec/ruby-stream_spec.rb
27
+ homepage:
28
+ licenses: []
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 2.0.0
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: Lazy stream implementation for Ruby
50
+ test_files: []