b-lazy 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,14 @@
1
+ =========================================================
2
+ = b-lazy : Why work hard for lazy evaluation? =
3
+ =========================================================
4
+
5
+ b-lazy extends a handful of core Ruby objects to provide
6
+ a more natural syntax for lazy evaluation.
7
+
8
+
9
+ Installation:
10
+ ==================
11
+
12
+ gem install --remote b-lazy
13
+
14
+
@@ -0,0 +1,15 @@
1
+
2
+ Gem::Specification.new do |s|
3
+ s.name = 'b-lazy'
4
+ s.version = '0.1.2'
5
+ s.date = '2011-02-04'
6
+ s.summary = "Why work hard for lazy-evaluation?"
7
+ s.description = "Extends core Ruby objects to provide inherent support for lazy-evaluation."
8
+ s.authors = ["Brian Lauber"]
9
+ s.email = 'constructible.truth@gmail.com'
10
+ s.homepage = 'http://b-lazy.rubyforge.org'
11
+ s.files = Dir['{specs/*,lib/*}'] + ['README','b-lazy.gemspec']
12
+ s.required_ruby_version = '>= 1.9.1'
13
+ s.rubyforge_project = 'b-lazy'
14
+ end
15
+
@@ -300,6 +300,10 @@ module Enumerable
300
300
  end
301
301
 
302
302
 
303
+
304
+
305
+
306
+
303
307
  # When #to_enum is called on an Enumerator, it creates a copy
304
308
  # and rewinds it (when possible). Unfortunately, this is
305
309
  # not actually the behavior that we want; we just want to make
@@ -312,6 +316,9 @@ module Enumerable
312
316
  self.to_enum
313
317
  end
314
318
  end
319
+
320
+
321
+
315
322
 
316
323
  end
317
324
 
@@ -344,3 +351,24 @@ class Integer
344
351
  end
345
352
 
346
353
  end
354
+
355
+
356
+
357
+
358
+ module BLazy
359
+
360
+ # Occassionally, we find that it is necessary to compute all values upfront
361
+ # before the enumeration. For example, if we are extracting lines from a
362
+ # file, then we might decide that guaranteeing that the file is closed cleanly
363
+ # is more important than lazily enumerating.
364
+ #
365
+ # This function serves as a compromise: compute all of the values, but only
366
+ # do so if I attempt to enumerate over them.
367
+ def when_needed(&f)
368
+ Enumerator.new do |out|
369
+ values = f.call
370
+ values.each{|v| out.yield v}
371
+ end
372
+ end
373
+
374
+ end
File without changes
@@ -0,0 +1,298 @@
1
+
2
+ require 'b-lazy'
3
+
4
+ describe Enumerable do
5
+
6
+ let(:list) { (1..10).to_a }
7
+ let(:mapping) { Proc.new{|x| 2 * x} }
8
+ let(:filter) { Proc.new{|x| x % 2 == 0} }
9
+
10
+ let(:left) { 3 }
11
+ let(:left_cond) { Proc.new{|x| x >= left} }
12
+
13
+ let(:right) { 8 }
14
+ let(:right_cond) { Proc.new{|x| x <= right} }
15
+
16
+
17
+ context "#lmap" do
18
+
19
+ it "produces one value for each input" do
20
+ list.lmap(&mapping).to_a.size.should == list.size
21
+ end
22
+
23
+ it "yields the same elements as #map" do
24
+ list.lmap(&mapping).to_a.should == list.map(&mapping)
25
+ end
26
+ end
27
+
28
+
29
+ context "#touch" do
30
+ it "ignores the return value of the block" do
31
+ list.touch{|x| 'ignore'}.to_a.should == list
32
+ end
33
+
34
+ it "allows for the modification of object state" do
35
+ [[1, 2, 3], [4, 5, 6]].touch{|x| x.pop}.to_a.should == [[1, 2], [4, 5]]
36
+ end
37
+ end
38
+
39
+
40
+ context "#lselect" do
41
+ it "yields the same elements as #select" do
42
+ list.lselect(&filter).to_a.should == list.select(&filter)
43
+ end
44
+
45
+ it "returns all elements when every element satisfies the filter" do
46
+ list.lselect{true}.to_a.should == list
47
+ end
48
+
49
+ it "returns no elements when none of the elements satisfy the filter" do
50
+ list.lselect{false}.should be_empty
51
+ end
52
+ end
53
+
54
+
55
+ context "#lreject" do
56
+ it "yields the same elements as #reject" do
57
+ list.lreject(&filter).to_a.should == list.reject(&filter)
58
+ end
59
+
60
+ it "returns all elements when none of the elements satisfy the filter" do
61
+ list.lreject{false}.to_a.should == list
62
+ end
63
+
64
+ it "returns no elements when every element satisfies the filter" do
65
+ list.lreject{true}.should be_empty
66
+ end
67
+ end
68
+
69
+
70
+
71
+
72
+ context "#start_when" do
73
+
74
+ it "starts with the element that satisfied the condition" do
75
+ list.start_when(&left_cond).peek.should == left
76
+ end
77
+
78
+ it "does not care if subsequent elements fail the condition" do
79
+ [1, 2, 3, 2, 1].start_when(&left_cond).reject(&left_cond).should_not be_empty
80
+ end
81
+
82
+ it "yields no elements if the condition is never satisfied" do
83
+ list.start_when{false}.should be_empty
84
+ end
85
+
86
+ end
87
+
88
+
89
+ context "#start_after" do
90
+
91
+ it "starts with the element after the one that satisfied the condition" do
92
+ list.start_after(&left_cond).peek.should == left + 1
93
+ end
94
+
95
+ it "does not care if subsequent elements fail the condition" do
96
+ [1, 2, 3, 2, 1].start_after(&left_cond).reject(&left_cond).should_not be_empty
97
+ end
98
+
99
+ it "yields no elements if the condition is never satisfied" do
100
+ list.start_after{false}.should be_empty
101
+ end
102
+
103
+ end
104
+
105
+
106
+
107
+
108
+ context "#do_while" do
109
+
110
+ it "only yields elements for which the condition is true" do
111
+ list.do_while(&right_cond).reject(&right_cond).should be_empty
112
+ end
113
+
114
+ it "stops yielding elements once the condition becomes false" do
115
+ [1, 2, 3, 4, 5].do_while{|x| x <= 3}.to_a.should == [1, 2, 3]
116
+ end
117
+
118
+ it "yields all elements if the condition is always satisfied" do
119
+ list.do_while{true}.to_a.should == list
120
+ end
121
+ end
122
+
123
+
124
+ context "#do_until" do
125
+ it "only yields elements for which the condition is false" do
126
+ list.do_until(&left_cond).select(&left_cond).should be_empty
127
+ end
128
+
129
+ it "stops yielding elements once the condition becomes true" do
130
+ [1, 2, 3, 4, 5].do_until{|x| x >= 3}.to_a.should == [1, 2]
131
+ end
132
+
133
+
134
+ it "yields no elements if the condition is immediately satisfied" do
135
+ list.do_until{true}.should be_empty
136
+ end
137
+
138
+
139
+ it "yields all elements if the condition is never satisfied" do
140
+ list.do_until{false}.to_a.should == list
141
+ end
142
+ end
143
+
144
+
145
+ context "#stop_when" do
146
+ it "only satisfies the condition with its final element" do
147
+ results = list.stop_when(&left_cond).to_a.map(&left_cond)
148
+
149
+ # The last element is true
150
+ results[-1].should == true
151
+
152
+ # All other elements are false
153
+ results.pop
154
+ results.should == [false] * results.length
155
+ end
156
+
157
+ it "yields exactly one element if the condition is immediately true" do
158
+ list.stop_when{true}.to_a.length.should == 1
159
+ end
160
+
161
+ it "yields all elements if the condition is never satisfied" do
162
+ list.stop_when{false}.to_a.should == list
163
+ end
164
+ end
165
+
166
+
167
+ context "#skip" do
168
+ it "yields all elements when n = 0" do
169
+ list.skip(0).to_a.should == list
170
+ end
171
+
172
+ it "yields nothing when n exceeds the number of elements" do
173
+ list.skip(list.size + 100).to_a.should be_empty
174
+ end
175
+
176
+ it "yields [total elements - n] elements when n <= [total elements]" do
177
+ list.skip(3).to_a.size.should == list.size - 3
178
+ end
179
+
180
+ it "omits the first n elements" do
181
+ list.skip(3).to_a.should == list[3..-1]
182
+ end
183
+ end
184
+
185
+
186
+ context "#cons" do
187
+
188
+ it "combines the elements of Enumerable objects into one Enumerator" do
189
+ [[1, 2, 3], [4, 5], [6]].cons.to_a.should == [1, 2, 3, 4, 5, 6]
190
+ end
191
+
192
+ it "yields nothing when given an empty Enumerable object" do
193
+ [].cons.should be_empty
194
+ end
195
+
196
+ end
197
+
198
+
199
+ context "#weave" do
200
+
201
+ it "repeatedly takes one element from each Enumerable" do
202
+ [[1, 2, 3], [4, 5, 6], [7, 8, 9]].weave.to_a.should == [1, 4, 7, 2, 5, 8, 3, 6, 9]
203
+ end
204
+
205
+ it "discards an Enumerable once it is empty" do
206
+ [[1, 2, 3], [4], [7, 8, 9]].weave.to_a.should == [1, 4, 7, 2, 8, 3, 9]
207
+ end
208
+ end
209
+
210
+
211
+ context "#diagonalize" do
212
+
213
+ it "yields nothing when all Enumerables are empty" do
214
+ [[], [], []].diagonalize.should be_empty
215
+ end
216
+
217
+
218
+
219
+ it "gradually introduces new Enumerables" do
220
+ source = [(1..10).to_a] * 10
221
+ source.diagonalize.take(10).should == [1, 1, 2, 1, 2, 3, 1, 2, 3, 4]
222
+ end
223
+
224
+ it "handles size mismatches without any surprises" do
225
+
226
+ end
227
+
228
+ end
229
+
230
+
231
+ context "#randomly" do
232
+
233
+ it "yields each element exactly once" do
234
+ r = list.randomly.to_a
235
+ list.each{|x| r.count(x).should == 1}
236
+ end
237
+
238
+ it "yields the elements in a random order" do
239
+ # This test has a 1 in 2^100 chance of failing. Meh.
240
+ (1..100).randomly.to_a.should_not == (1..100)
241
+ end
242
+
243
+ end
244
+
245
+
246
+ context "#ltranspose" do
247
+
248
+ it "yields arrays with length equal to the number of Enumerables provided" do
249
+ [[1, 2, 3], [4, 5, 6]].ltranspose.map{|x| x.size}.should == [2, 2, 2]
250
+ end
251
+
252
+ it "yields zero elements when one of its Enumerables is already empty" do
253
+ [[1, 2, 3], [], [7, 8, 9]].ltranspose.should be_empty
254
+ end
255
+
256
+ end
257
+
258
+
259
+ context "#cycle" do
260
+ it "indefinitely repeats the same sequence" do
261
+ (1..3).cycle.take(9).should == [1, 2, 3, 1, 2, 3, 1, 2, 3]
262
+ end
263
+
264
+ it "yields nothing when performed on an empty Enumerator" do
265
+ [].cycle.take(3).should == []
266
+ end
267
+ end
268
+
269
+ context "#repeat" do
270
+
271
+ it "repeats the sequence exactly n times" do
272
+ (1..3).repeat(3).to_a.should == [1, 2, 3, 1, 2, 3, 1, 2, 3]
273
+ end
274
+
275
+ it "yields nothing when n is less than 1" do
276
+ (1..3).repeat(0).to_a.should == []
277
+ end
278
+
279
+ it "treats floating-point values of n as floor(n)" do
280
+ (1..3).repeat(2.2).to_a.should == [1, 2, 3, 1, 2, 3]
281
+ end
282
+
283
+ end
284
+
285
+
286
+ context "#ensure_enum" do
287
+ it "returns self when called on an Enumerator" do
288
+ x = [1, 2, 3].each
289
+ x.ensure_enum.should be_equal(x)
290
+ end
291
+
292
+ it "returns a new Enumerator when called on an Enumerable" do
293
+ x = [1, 2, 3]
294
+ x.ensure_enum.should_not be_equal(x)
295
+ end
296
+ end
297
+
298
+ end
@@ -0,0 +1,64 @@
1
+
2
+ require 'b-lazy'
3
+
4
+ describe Integer do
5
+
6
+ context "#positives" do
7
+ it "starts at 1" do
8
+ Integer.positives.next.should == 1
9
+ end
10
+
11
+ it "produces an ascending sequence" do
12
+ Integer.positives.grab(10).should == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
13
+ end
14
+ end
15
+
16
+
17
+ context "#non_negatives" do
18
+ it "starts at 0" do
19
+ Integer.non_negatives.next.should == 0
20
+ end
21
+
22
+ it "produces an ascending sequence" do
23
+ Integer.non_negatives.grab(10).should == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
24
+ end
25
+ end
26
+
27
+
28
+
29
+
30
+
31
+ context "#negatives" do
32
+ it "starts at -1" do
33
+ Integer.negatives.next.should == -1
34
+ end
35
+
36
+ it "produces a descending sequence" do
37
+ Integer.negatives.grab(10).should == [-1, -2, -3, -4, -5, -6, -7, -8, -9, -10]
38
+ end
39
+ end
40
+
41
+
42
+ context "#non_positives" do
43
+ it "starts at 0" do
44
+ Integer.non_positives.next.should == 0
45
+ end
46
+
47
+ it "produces an ascending sequences" do
48
+ Integer.non_positives.grab(10).should == [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
49
+ end
50
+ end
51
+
52
+
53
+
54
+ context "#all" do
55
+ it "starts at 0" do
56
+ Integer.all.next.should == 0
57
+ end
58
+
59
+ it "alternates between positives and negatives" do
60
+ Integer.all.grab(10).should == [0, 1, -1, 2, -2, 3, -3, 4, -4, 5]
61
+ end
62
+ end
63
+
64
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 1
9
- version: 0.1.1
8
+ - 2
9
+ version: 0.1.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Brian Lauber
@@ -27,7 +27,12 @@ extensions: []
27
27
  extra_rdoc_files: []
28
28
 
29
29
  files:
30
+ - specs/enumerable_spec.rb
31
+ - specs/blazy_spec.rb
32
+ - specs/integer_spec.rb
30
33
  - lib/b-lazy.rb
34
+ - README
35
+ - b-lazy.gemspec
31
36
  has_rdoc: true
32
37
  homepage: http://b-lazy.rubyforge.org
33
38
  licenses: []