deferred_enum 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in deferred_enum.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'rspec'
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Alexey Mikhaylov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,34 @@
1
+ == Synopsys
2
+ Ruby Enumerable extension. Main idea is lazy computations within enumerators.
3
+
4
+ == Usage
5
+ Install as a gem:
6
+ sudo gem install deferred_enum
7
+
8
+ This gem introduces DeferredEnumerator class:
9
+ ary = [1, 2, 3, 4]
10
+ deferred = ary.defer # #<DeferredEnumerator: [1, 2, 3, 4]:each>
11
+
12
+ DeferredEnumerator brings some optimizations to all?, any? and none? predicates
13
+ deferred.all?(&:even?) # Will stop iteration after first false-result = 1 iteration
14
+ deferred.none?(&:even?) # 2 iterations
15
+ deferred.any?(&:even?) # 2 iterations
16
+
17
+ It also introduces lazy versions of Enumerable's #select, #map and #reject methods
18
+ deferred.map { |i| i + 1 } # #<DeferredEnumerator: #<Enumerator::Generator>:each>
19
+ deferred.select { |i| i.even? } # #<DeferredEnumerator: #<Enumerator::Generator>:each>
20
+ deferred.reject { |i| i.odd? } # #<DeferredEnumerator: #<Enumerator::Generator>:each>
21
+
22
+ So you can safely chain your filters, they won't be treated as arrays:
23
+ deferred.map(&:succ).select(&:even?) # #<DeferredEnumerator: #<Enumerator::Generator>:each>
24
+
25
+ You can build chains of Enumerables:
26
+ deferred.concat([2]).to_a # [1, 2, 3, 4, 2]
27
+
28
+ Or append elements to the end of enumerator:
29
+ deferred << 2
30
+
31
+ You can even remove duplicates from enumerator, though this operation can be tough:
32
+ deferred.uniq # #<DeferredEnumerator: #<Enumerator::Generator>:each>
33
+
34
+ There are many other methods in DeferredEnumerator, please refer to documentation.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "deferred_enum/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "deferred_enum"
7
+ s.version = DeferredEnum::VERSION
8
+ s.authors = ["Alexey Mikhaylov"]
9
+ s.email = ["amikhailov83@gmail.com"]
10
+ s.homepage = "https://github.com/take-five/deferred_enum"
11
+ s.summary = %q{Introduces lazy computations to Enumerable}
12
+ s.description = File.read(File.expand_path('../README.rdoc', __FILE__))
13
+ s.date = Time.now.strftime('%Y-%m-%d')
14
+
15
+ s.rubyforge_project = "deferred_enum"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_development_dependency "rspec"
23
+ end
@@ -0,0 +1 @@
1
+ require "deferred_enumerable"
@@ -0,0 +1,5 @@
1
+ module Enumerable
2
+ def defer(method_id = :each)
3
+ DeferredEnumerator.new(self, method_id)
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module DeferredEnum
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,267 @@
1
+ module DeferredEnumerable
2
+ include Enumerable
3
+
4
+ # Passes each element of the collection to the given block.
5
+ # The method returns true if the block never returns false or nil.
6
+ def all?(&blk) # :yields: obj
7
+ blk ||= proc { |e| e }
8
+ each { |entry| return false unless blk.call(entry) }
9
+ true
10
+ end
11
+
12
+ # Passes each element of the collection to the given block.
13
+ # The method returns true if the block ever returns a value other than false or nil.
14
+ def any?(&blk) # :yields: obj
15
+ blk ||= proc { |e| e }
16
+ each { |entry| return true if blk.call(entry) }
17
+ false
18
+ end
19
+
20
+ # Passes each element of the collection to the given block.
21
+ # The method returns true if the block never returns true for all elements.
22
+ # If the block is not given, none? will return true only if none of the collection members is true.
23
+ def none?(&blk)
24
+ blk ||= proc { |e| e }
25
+ each { |entry| return false if blk.call(entry) }
26
+ true
27
+ end
28
+
29
+ # Returns a new enumerator with the results of running block once for every element in enum.
30
+ def collect # :yields: obj
31
+ return self unless block_given?
32
+
33
+ DeferredEnumerator.new do |yielder|
34
+ each { |entry| yielder << yield(entry) }
35
+ end
36
+ end
37
+ alias map collect
38
+
39
+ # Returns a enumerator containing all elements of enum for which block is not false
40
+ def select # :yields: obj
41
+ return self unless block_given?
42
+
43
+ DeferredEnumerator.new do |yielder|
44
+ each { |entry| yielder << entry if yield(entry) }
45
+ end
46
+ end
47
+ alias find_all select
48
+
49
+ # Returns a enumerator containing all elements of enum for which block is false
50
+ def reject # :yields: obj
51
+ return self unless block_given?
52
+
53
+ DeferredEnumerator.new do |yielder|
54
+ each { |entry| yielder << entry unless yield(entry) }
55
+ end
56
+ end
57
+
58
+ # Returns a new enumerator that is a one-dimensional flattening of this enumerator (recursively).
59
+ # That is, for every element that is an Enumerable, extract its elements into the new enumerator.
60
+ # If the optional level argument determines the level of recursion to flatten.
61
+ def flatten(level = nil)
62
+ do_recursion, next_level = if level.is_a?(Fixnum)
63
+ [level > 0, level - 1]
64
+ else
65
+ [true, nil]
66
+ end
67
+
68
+ DeferredEnumerator.new do |yielder|
69
+ each do |entry|
70
+ if entry.is_a?(Enumerable) && do_recursion
71
+ entry.defer.flatten(next_level).each { |nested| yielder << nested }
72
+ else
73
+ yielder << entry
74
+ end
75
+ end
76
+ end # Enumerator.new
77
+ end # def flatten
78
+
79
+ # Returns a new enumerator with the concatenated results of running block once for every element in enum.
80
+ def flat_map(&blk) # :yields: obj
81
+ return flatten unless block_given?
82
+
83
+ flatten.collect(&blk)
84
+ end
85
+ alias collect_concat flat_map
86
+
87
+ # Calls block for each element of enum repeatedly n times or forever if none or nil is given.
88
+ # If a non-positive number is given or the collection is empty, does nothing.
89
+ # Returns nil if the loop has finished without getting interrupted.
90
+ #
91
+ # Unlike <tt>Enumerable#cycle</tt> <tt>DeferredEnumerable#cycle</tt> DOES NOT save elements in an internal array.
92
+ #
93
+ # If no block is given, an enumerator is returned instead.
94
+ def cycle(n = nil) # :yields: obj
95
+ cycles_num = n.is_a?(Fixnum) ? n : nil
96
+
97
+ enum = DeferredEnumerator.new do |yielder|
98
+ while cycles_num.nil? || cycles_num > 0
99
+ each { |entry| yielder << entry }
100
+
101
+ cycles_num -= 1 if cycles_num
102
+ end
103
+ end
104
+
105
+ unless block_given?
106
+ enum
107
+ else
108
+ enum.each { |e| yield(e) }
109
+
110
+ nil
111
+ end
112
+ end
113
+
114
+ # Drops first n elements from enum, and returns rest elements in an enumerator.
115
+ def drop(n)
116
+ raise TypeError, 'Integer (> 0) expected' unless n.is_a?(Fixnum) && n > 0
117
+
118
+ DeferredEnumerator.new do |yielder|
119
+ each { |entry| yielder << entry if (n -= 1) < 0}
120
+ end
121
+ end
122
+
123
+ # Returns first n elements from enum.
124
+ def take(n)
125
+ raise TypeError, 'Integer (> 0) expected' unless n.is_a?(Fixnum) && n > 0
126
+
127
+ DeferredEnumerator.new do |yielder|
128
+ each { |entry| yielder << entry if (n -= 1) >= 0}
129
+ end
130
+ end
131
+
132
+ # Drops elements up to, but not including, the first element for which the block returns nil
133
+ # or false and returns an enumerator containing the remaining elements.
134
+ def drop_while # :yields: obj
135
+ return self unless block_given?
136
+
137
+ DeferredEnumerator.new do |yielder|
138
+ keep = false
139
+ each do |entry|
140
+ keep ||= !yield(entry)
141
+
142
+ yielder << entry if keep
143
+ end
144
+ end
145
+ end
146
+
147
+ # Passes elements to the block until the block returns <code>nil</code> or <code>false</code>,
148
+ # then stops iterating and returns an enumerator of all prior elements.
149
+ def take_while # :yields: obj
150
+ return self unless block_given?
151
+
152
+ DeferredEnumerator.new do |yielder|
153
+ each do |entry|
154
+ break unless yield(entry)
155
+
156
+ yielder << entry
157
+ end
158
+ end
159
+ end
160
+
161
+ # Returns an array of every element in enum for which <code>Pattern === element</code>.
162
+ # If the optional <code>block</code> is supplied, each matching element is passed to it,
163
+ # and the block's result is stored in the output array.
164
+ def grep(pattern, &blk) # :yields: obj
165
+ filtered = select { |obj| pattern === obj }
166
+
167
+ block_given? ?
168
+ filtered.collect(&blk) :
169
+ filtered
170
+ end
171
+
172
+ # Returns <code>true</code> if any member of <code>enum</code> equals <code>obj</code>.
173
+ # Equality is tested using <code>==</code>.
174
+ def include?(obj) # :yields: obj
175
+ any? { |e| obj == e }
176
+ end
177
+ alias member? include?
178
+
179
+ # Returns two enums, the first containing the elements of enum for which the block evaluates to true, the second containing the rest.
180
+ def partition # :yields: obj
181
+ super.map(&:defer)
182
+ end
183
+
184
+ # Returns an enumerator containing the items in enum sorted,
185
+ # either according to their own <=> method,
186
+ # or by using the results of the supplied block.
187
+ def sort
188
+ super.defer
189
+ end
190
+
191
+ # Sorts enum using a set of keys generated by mapping the values in enum through the given block.
192
+ def sort_by
193
+ super.defer
194
+ end
195
+
196
+ # Takes one element from <i>enum</i> and merges corresponding
197
+ # elements from each <i>enumerables</i>. This generates a sequence of
198
+ # <em>n</em>-element arrays, where <em>n</em> is one more than the
199
+ # count of arguments. The length of the resulting sequence will be
200
+ # <code>enum#size</code>. If the size of any argument is less than
201
+ # <code>enum#size</code>, <code>nil</code> values are supplied. If
202
+ # a block is given, it is invoked for each output array, otherwise
203
+ # an enumerator of arrays is returned.
204
+ def zip(*enumerables)
205
+ return super if block_given?
206
+
207
+ raise TypeError, 'Zip accepts only enumerables' unless enumerables.all? { |e| e.is_a?(Enumerable) }
208
+
209
+ deferred = enumerables.map(&:defer)
210
+
211
+ DeferredEnumerator.new do |yielder|
212
+ each do |entry|
213
+ ary = [entry]
214
+
215
+ deferred.each do |enum|
216
+ ary << begin
217
+ enum.next
218
+ rescue StopIteration
219
+ nil
220
+ end
221
+ end
222
+
223
+ yielder << ary
224
+ end
225
+ end
226
+ end
227
+
228
+ # Returns a enumerator with all nil elements removed.
229
+ def compact
230
+ reject(&:nil?)
231
+ end
232
+
233
+ # Appends the <code>enumerables</code> to self.
234
+ def concat(*enumerables)
235
+ raise TypeError, 'DeferredEnumerabler#concat accepts only enumerables' unless enumerables.all? { |e| e.is_a?(Enumerable) }
236
+
237
+ DeferredEnumerator.new do |yielder|
238
+ [self, *enumerables].each do |enum|
239
+ enum.each { |entry| yielder << entry }
240
+ end
241
+ end
242
+ end
243
+ alias chain concat
244
+ alias + concat
245
+
246
+ # Appends the <code>element</code> to end of enumerator
247
+ def push(element)
248
+ concat([element])
249
+ end
250
+ alias << push
251
+
252
+ # Returns a new enumerator by removing duplicate values in self.
253
+ def uniq
254
+ values = {}
255
+
256
+ select { |entry|
257
+ value = block_given? ? yield(entry) : entry
258
+
259
+ unless values.has_key?(value)
260
+ values.store(value, true)
261
+ end
262
+ }
263
+ end
264
+ end
265
+
266
+ require "deferred_enumerator"
267
+ require "deferred_enum/enumerable"
@@ -0,0 +1,6 @@
1
+ require "deferred_enumerable"
2
+ require "deferred_enum/enumerable"
3
+
4
+ class DeferredEnumerator < Enumerator
5
+ include ::DeferredEnumerable
6
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe Enumerable do
4
+ it "should have defer method" do
5
+ ary = []
6
+ ary.should respond_to(:defer)
7
+ end
8
+
9
+ it "should return DeferredEnumerable" do
10
+ [].defer.should be_a(DeferredEnumerable)
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe DeferredEnumerator do
4
+ it "should drop elements from collections" do
5
+ [1, 2, 3, 4, 3].defer.drop(3).to_a.should == [4, 3]
6
+
7
+ [1, 2, 3, 4, 3].defer.drop_while {|n| n <= 3 }.to_a.should == [4, 3]
8
+ end
9
+
10
+ it "should take first elements from collections" do
11
+ [4, 3, 2, 1, 4].defer.take(3).to_a.should == [4, 3, 2]
12
+
13
+ [4, 3, 2, 1, 4].defer.take_while { |n| n > 1 }.to_a.should == [4, 3, 2]
14
+ end
15
+ end
@@ -0,0 +1,51 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe DeferredEnumerator do
4
+ it "should map collections" do
5
+ [1, 2, 3].defer.map { |i| i + 1 }.to_a.should == [2, 3, 4]
6
+ end
7
+
8
+ it "should filter collections" do
9
+ [1, 2, 3].defer.select { |i| i.odd? }.to_a.should == [1, 3]
10
+ end
11
+
12
+ it "should reject elements from collections" do
13
+ [1, 2, 3].defer.reject { |i| i.odd? }.to_a.should == [2]
14
+ end
15
+
16
+ it "should flatten collections" do
17
+ [[1, 2], 3, [4]].defer.flatten.to_a.should == [1, 2, 3, 4]
18
+
19
+ [[1, [2]], 3].defer.flatten(1).to_a.should == [1, [2], 3]
20
+ end
21
+
22
+ it "should flat and map collections" do
23
+ [[1, 2], 3, [4]].defer.flat_map { |i| i + 1 }.to_a.should == [2, 3, 4, 5]
24
+ end
25
+
26
+ it "should cycle collections" do
27
+ [1].defer.cycle(2).to_a.should == [1, 1]
28
+
29
+ counts = 0
30
+ [1].defer.cycle { counts += 1; break if counts == 10 }
31
+ counts.should == 10
32
+ end
33
+
34
+ it "should grep collections" do
35
+ ['abc', 'def', 'ghi'].defer.grep(/abc/).to_a.should == ['abc']
36
+ end
37
+
38
+ it "should compact collections" do
39
+ [1, 2, nil].defer.compact.to_a.should == [1, 2]
40
+ end
41
+
42
+ it "should chain collections" do
43
+ [1, 2].defer.chain([3], [], [4]).to_a.should == [1, 2, 3, 4]
44
+ end
45
+
46
+ it "should remove duplicates from collections" do
47
+ [1, 2, 3, 4, 3, 4].defer.uniq.to_a.should == [1, 2, 3, 4]
48
+
49
+ [1, 2, 3, 4].defer.uniq { |n| n.even? }.to_a.should == [1, 2]
50
+ end
51
+ end
@@ -0,0 +1,24 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe DeferredEnumerator do
4
+ it "should lazily execute all? method" do
5
+ counts = 0
6
+
7
+ [2, 3, 4].all? { |e| counts += 1; e.even? }.should == false
8
+ counts.should == 2
9
+ end
10
+
11
+ it "should lazily execute any? method" do
12
+ counts = 0
13
+
14
+ [2, 3, 4].any? { |e| counts += 1; e.odd? }.should == true
15
+ counts.should == 2
16
+ end
17
+
18
+ it "should lazily execute none? method" do
19
+ counts = 0
20
+
21
+ [2, 3, 4].none? { |e| counts += 1; e.odd? }.should == false
22
+ counts.should == 2
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.setup
4
+
5
+ require "deferred_enum"
data/spec/zip_spec.rb ADDED
@@ -0,0 +1,10 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ describe DeferredEnumerator do
4
+ it "should zip collections" do
5
+ seq1 = [2, 3, 4]
6
+ seq2 = [3, 4]
7
+
8
+ [1, 2, 3].defer.zip(seq1, seq2).to_a.should == [[1, 2, 3], [2, 3, 4], [3, 4, nil]]
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deferred_enum
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Alexey Mikhaylov
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-12-11 00:00:00 +06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ description: |-
34
+ == Synopsys
35
+ Ruby Enumerable extension. Main idea is lazy computations within enumerators.
36
+
37
+ == Usage
38
+ Install as a gem:
39
+ sudo gem install deferred_enum
40
+
41
+ This gem introduces DeferredEnumerator class:
42
+ ary = [1, 2, 3, 4]
43
+ deferred = ary.defer # #<DeferredEnumerator: [1, 2, 3, 4]:each>
44
+
45
+ DeferredEnumerator brings some optimizations to all?, any? and none? predicates
46
+ deferred.all?(&:even?) # Will stop iteration after first false-result = 1 iteration
47
+ deferred.none?(&:even?) # 2 iterations
48
+ deferred.any?(&:even?) # 2 iterations
49
+
50
+ It also introduces lazy versions of Enumerable's #select, #map and #reject methods
51
+ deferred.map { |i| i + 1 } # #<DeferredEnumerator: #<Enumerator::Generator>:each>
52
+ deferred.select { |i| i.even? } # #<DeferredEnumerator: #<Enumerator::Generator>:each>
53
+ deferred.reject { |i| i.odd? } # #<DeferredEnumerator: #<Enumerator::Generator>:each>
54
+
55
+ So you can safely chain your filters, they won't be treated as arrays:
56
+ deferred.map(&:succ).select(&:even?) # #<DeferredEnumerator: #<Enumerator::Generator>:each>
57
+
58
+ You can build chains of Enumerables:
59
+ deferred.concat([2]).to_a # [1, 2, 3, 4, 2]
60
+
61
+ Or append elements to the end of enumerator:
62
+ deferred << 2
63
+
64
+ You can even remove duplicates from enumerator, though this operation can be tough:
65
+ deferred.uniq # #<DeferredEnumerator: #<Enumerator::Generator>:each>
66
+
67
+ There are many other methods in DeferredEnumerator, please refer to documentation.
68
+ email:
69
+ - amikhailov83@gmail.com
70
+ executables: []
71
+
72
+ extensions: []
73
+
74
+ extra_rdoc_files: []
75
+
76
+ files:
77
+ - .gitignore
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.rdoc
81
+ - Rakefile
82
+ - deferred_enum.gemspec
83
+ - lib/deferred_enum.rb
84
+ - lib/deferred_enum/enumerable.rb
85
+ - lib/deferred_enum/version.rb
86
+ - lib/deferred_enumerable.rb
87
+ - lib/deferred_enumerator.rb
88
+ - spec/defer_spec.rb
89
+ - spec/drop_take_spec.rb
90
+ - spec/map_select_spec.rb
91
+ - spec/predicates_spec.rb
92
+ - spec/test_helper.rb
93
+ - spec/zip_spec.rb
94
+ has_rdoc: true
95
+ homepage: https://github.com/take-five/deferred_enum
96
+ licenses: []
97
+
98
+ post_install_message:
99
+ rdoc_options: []
100
+
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ requirements: []
120
+
121
+ rubyforge_project: deferred_enum
122
+ rubygems_version: 1.3.7
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: Introduces lazy computations to Enumerable
126
+ test_files: []
127
+