totally_lazy 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/Gemfile +3 -0
- data/Guardfile +24 -0
- data/README.md +31 -0
- data/Rakefile +12 -0
- data/VERSION +1 -1
- data/lib/any.rb +13 -0
- data/lib/generators.rb +161 -0
- data/lib/option.rb +75 -10
- data/lib/pair.rb +6 -0
- data/lib/parallel/parallel.rb +442 -0
- data/lib/parallel/processor_count.rb +85 -0
- data/lib/predicates/compare.rb +32 -0
- data/lib/{predicates.rb → predicates/conversions.rb} +0 -13
- data/lib/predicates/numbers.rb +25 -0
- data/lib/predicates/predicates.rb +57 -0
- data/lib/predicates/where.rb +34 -0
- data/lib/predicates/where_processor.rb +13 -0
- data/lib/sequence.rb +217 -21
- data/lib/totally_lazy.rb +124 -1
- data/spec/functor_spec.rb +35 -0
- data/spec/generators_spec.rb +37 -0
- data/spec/option_spec.rb +73 -2
- data/spec/pair_spec.rb +5 -2
- data/spec/predicate_spec.rb +37 -5
- data/spec/sequence_spec.rb +144 -4
- data/spec/serialization_spec.rb +56 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/type_check_spec.rb +1 -2
- data/totally_lazy.gemspec +28 -4
- metadata +60 -3
data/lib/sequence.rb
CHANGED
@@ -6,9 +6,30 @@ end
|
|
6
6
|
|
7
7
|
module Sequences
|
8
8
|
|
9
|
+
# Creates a sequence
|
10
|
+
#
|
11
|
+
# == Parameters:
|
12
|
+
# items::
|
13
|
+
# Varargs - any valid ruby objects can be supplied
|
14
|
+
#
|
15
|
+
# == Returns:
|
16
|
+
# A sequence
|
17
|
+
#
|
18
|
+
# == Examples
|
19
|
+
#
|
20
|
+
# sequence(1,2,3,4).filter(even) # lazily returns 2,4
|
21
|
+
# sequence(1,2).map(as_string) # lazily returns "1","2"
|
22
|
+
# sequence(1, 2).map_concurrently(to_string) # lazily distributes the work to background threads
|
23
|
+
# sequence(1,2,3).take(2) # lazily returns 1,2
|
24
|
+
# sequence(1,2,3).drop(2) # lazily returns 3
|
25
|
+
# sequence(1,2,3).tail # lazily returns 2,3
|
26
|
+
# sequence(1,2,3).head # eagerly returns 1
|
27
|
+
# sequence(1,2,3).head_option # eagerly returns an option
|
28
|
+
# some(sequence(1,2,3)).get_or_else(empty) # eagerly returns value or else empty sequence
|
29
|
+
# sequence(1, 2, 3, 4, 5).filter(where(is greater_than 2).and(is odd)) # lazily returns 3,5
|
9
30
|
def sequence(*items)
|
10
31
|
if items.size == 1
|
11
|
-
if [Range, Hash].include?(items.first.class)
|
32
|
+
if [Range, Hash, Array, Set].include?(items.first.class)
|
12
33
|
Sequence.new(items.first)
|
13
34
|
elsif items.first.nil?
|
14
35
|
empty
|
@@ -20,10 +41,23 @@ module Sequences
|
|
20
41
|
end
|
21
42
|
end
|
22
43
|
|
44
|
+
# Creates an empty sequence
|
45
|
+
#
|
46
|
+
# == Returns:
|
47
|
+
# An empty sequence
|
48
|
+
#
|
49
|
+
# == Examples
|
50
|
+
#
|
51
|
+
# empty
|
52
|
+
# some(sequence(1,2,3)).get_or_else(empty) # eagerly returns value or else empty sequence
|
23
53
|
def empty
|
24
54
|
Empty.new
|
25
55
|
end
|
26
56
|
|
57
|
+
def deserialize(data)
|
58
|
+
sequence(data).deserialize
|
59
|
+
end
|
60
|
+
|
27
61
|
class Sequence < Enumerator
|
28
62
|
|
29
63
|
include Comparable
|
@@ -50,7 +84,7 @@ module Sequences
|
|
50
84
|
def map(predicate=nil, &block)
|
51
85
|
if predicate
|
52
86
|
Sequence.new(self) { |yielder, val|
|
53
|
-
v = predicate.call(val)
|
87
|
+
v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.call(val)
|
54
88
|
yielder << v unless v.nil?
|
55
89
|
}
|
56
90
|
else
|
@@ -62,10 +96,32 @@ module Sequences
|
|
62
96
|
|
63
97
|
alias collect map
|
64
98
|
|
99
|
+
# def reduce(operation_or_value=nil)
|
100
|
+
# case operation_or_value
|
101
|
+
# when Symbol
|
102
|
+
# # convert things like reduce(:+) into reduce { |s,e| s + e }
|
103
|
+
# return reduce { |s,e| s.send(operation_or_value, e) }
|
104
|
+
# when nil
|
105
|
+
# acc = nil
|
106
|
+
# else
|
107
|
+
# acc = operation_or_value
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# each do |a|
|
111
|
+
# if acc.nil?
|
112
|
+
# acc = a
|
113
|
+
# else
|
114
|
+
# acc = yield(acc, a)
|
115
|
+
# end
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# return acc
|
119
|
+
# end
|
120
|
+
|
65
121
|
def select(predicate=nil, &block)
|
66
122
|
if predicate
|
67
123
|
Sequence.new(self) { |yielder, val|
|
68
|
-
v = predicate.call(val)
|
124
|
+
v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.call(val)
|
69
125
|
yielder << v unless v.nil?
|
70
126
|
}
|
71
127
|
else
|
@@ -80,14 +136,23 @@ module Sequences
|
|
80
136
|
alias find_all select
|
81
137
|
alias filter select
|
82
138
|
|
83
|
-
def reject(&block)
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
139
|
+
def reject(predicate=nil, &block)
|
140
|
+
if predicate
|
141
|
+
Sequence.new(self) { |yielder, val|
|
142
|
+
v = predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates, true) : predicate.call(val, :self, true)
|
143
|
+
yielder << v unless v.nil?
|
144
|
+
}
|
145
|
+
else
|
146
|
+
Sequence.new(self) { |yielder, val|
|
147
|
+
unless block.call(val)
|
148
|
+
yielder << val
|
149
|
+
end
|
150
|
+
}
|
151
|
+
end
|
89
152
|
end
|
90
153
|
|
154
|
+
alias unfilter reject
|
155
|
+
|
91
156
|
def grep(pattern)
|
92
157
|
Sequence.new(self) { |yielder, val|
|
93
158
|
if pattern === val
|
@@ -146,7 +211,6 @@ module Sequences
|
|
146
211
|
def flat_map(&block)
|
147
212
|
Sequence.new(self) { |yielder, val|
|
148
213
|
ary = block.call(val)
|
149
|
-
# TODO: check ary is an Array
|
150
214
|
ary.each { |x|
|
151
215
|
yielder << x
|
152
216
|
}
|
@@ -197,6 +261,10 @@ module Sequences
|
|
197
261
|
sequence.empty? ? none : some(sequence.entries.last)
|
198
262
|
end
|
199
263
|
|
264
|
+
def contains?(value)
|
265
|
+
sequence.empty? ? false : sequence.entries.include?(value)
|
266
|
+
end
|
267
|
+
|
200
268
|
def tail
|
201
269
|
Sequence.new(Sequence::Generator.new do |g|
|
202
270
|
self.empty? ? raise(NoSuchElementException.new, 'The sequence was empty') : self.drop(1).each { |i| g.yield i }
|
@@ -218,18 +286,19 @@ module Sequences
|
|
218
286
|
|
219
287
|
def transpose
|
220
288
|
Sequence.new(Sequence::Generator.new do |g|
|
289
|
+
if self.empty?
|
290
|
+
raise(NoSuchElementException.new, 'The sequence was empty')
|
291
|
+
else
|
292
|
+
raise(Exception.new, 'The subject of transposition must be multidimensional') unless self.to_a.first.is_a?(Array)
|
293
|
+
end
|
221
294
|
result = []
|
222
|
-
|
295
|
+
max = option(self.to_a.max { |a, b| a.size <=> b.size })
|
296
|
+
max_size = max.get_or_throw(NoSuchElementException, 'The option was empty').size
|
223
297
|
max_size.times do |i|
|
224
298
|
result[i] = [self.to_a.first.size]
|
225
299
|
self.to_a.each_with_index { |r, j| result[i][j] = r[i] }
|
226
300
|
end
|
227
301
|
result
|
228
|
-
if self.empty?
|
229
|
-
raise(NoSuchElementException.new, 'The sequence was empty')
|
230
|
-
else
|
231
|
-
raise(Exception.new, 'The subject of transposition must be multidimensional') unless self.to_a.first.is_a?(Array)
|
232
|
-
end
|
233
302
|
result.each { |i| g.yield i }
|
234
303
|
end)
|
235
304
|
end
|
@@ -237,9 +306,7 @@ module Sequences
|
|
237
306
|
def join(target_sequence)
|
238
307
|
Sequence.new(Sequence::Generator.new do |g|
|
239
308
|
raise(Exception.new, 'The target (right side) must be a sequence') unless target_sequence.kind_of?(Sequences::Sequence)
|
240
|
-
|
241
|
-
elements = elements.reject { |i| i.is_a?(Empty) }
|
242
|
-
(elements << target_sequence.entries).flatten.each { |i| g.yield i }
|
309
|
+
self.entries.push(target_sequence.entries).flatten.each { |i| g.yield i unless i.is_a?(Empty) }
|
243
310
|
end)
|
244
311
|
end
|
245
312
|
|
@@ -263,7 +330,7 @@ module Sequences
|
|
263
330
|
|
264
331
|
def to_seq
|
265
332
|
Sequence.new(Sequence::Generator.new do |g|
|
266
|
-
self.entries.map { |e| Type.
|
333
|
+
self.entries.map { |e| Type.responds(e, :entries); e.entries }.flatten.each { |i| g.yield i }
|
267
334
|
end)
|
268
335
|
end
|
269
336
|
|
@@ -273,6 +340,24 @@ module Sequences
|
|
273
340
|
end)
|
274
341
|
end
|
275
342
|
|
343
|
+
def from_arrays
|
344
|
+
Sequence.new(Sequence::Generator.new do |g|
|
345
|
+
self.entries.map { |e| Type.check(e, Array); e }.flatten.each { |i| g.yield i }
|
346
|
+
end)
|
347
|
+
end
|
348
|
+
|
349
|
+
def from_sets
|
350
|
+
Sequence.new(Sequence::Generator.new do |g|
|
351
|
+
self.entries.map { |e| Type.check(e, Set); e.to_a }.flatten.each { |i| g.yield i }
|
352
|
+
end)
|
353
|
+
end
|
354
|
+
|
355
|
+
def in_pairs
|
356
|
+
Sequence.new(Sequence::Generator.new do |g|
|
357
|
+
self.each_slice(2) { |k, v| g.yield pair(k, v) }
|
358
|
+
end)
|
359
|
+
end
|
360
|
+
|
276
361
|
def to_a
|
277
362
|
execution = {
|
278
363
|
Sequences::Sequence => -> { self.entries.map { |s| s.entries } },
|
@@ -287,6 +372,26 @@ module Sequences
|
|
287
372
|
end
|
288
373
|
end
|
289
374
|
|
375
|
+
def marshal_dump
|
376
|
+
serialize
|
377
|
+
end
|
378
|
+
|
379
|
+
def marshal_load(data)
|
380
|
+
Sequence.new(data).deserialize
|
381
|
+
end
|
382
|
+
|
383
|
+
def serialize
|
384
|
+
c = []
|
385
|
+
serializer(c, self.entries)
|
386
|
+
c
|
387
|
+
end
|
388
|
+
|
389
|
+
def deserialize
|
390
|
+
c = []
|
391
|
+
deserializer(c, self.entries)
|
392
|
+
Sequence.new(c)
|
393
|
+
end
|
394
|
+
|
290
395
|
def all
|
291
396
|
to_a.flatten
|
292
397
|
end
|
@@ -299,8 +404,42 @@ module Sequences
|
|
299
404
|
blank?(sequence[index]) ? none : some(sequence[index])
|
300
405
|
end
|
301
406
|
|
407
|
+
def get_or_throw(index, exception, message='')
|
408
|
+
blank?(sequence[index]) ? raise(exception, message) : sequence[index]
|
409
|
+
end
|
410
|
+
|
411
|
+
def drop_nil
|
412
|
+
Sequence.new(Sequence::Generator.new do |g|
|
413
|
+
self.reject { |e| e.nil? }.each { |i| g.yield i }
|
414
|
+
end)
|
415
|
+
end
|
416
|
+
|
417
|
+
def map_concurrently(predicate=nil, options={}, &block)
|
418
|
+
if predicate
|
419
|
+
Sequence.new(Sequence::Generator.new do |g|
|
420
|
+
Parallel.map(self.entries, options) { |val|
|
421
|
+
predicate.is_a?(WherePredicate) ? WhereProcessor.new(val).apply(predicate.predicates) : predicate.call(val)
|
422
|
+
}.each { |i| g.yield i unless i.nil? }
|
423
|
+
end)
|
424
|
+
else
|
425
|
+
Sequence.new(Sequence::Generator.new do |g|
|
426
|
+
Parallel.map(self.entries, options) { |val| block.call(val) }.each { |i| g.yield i }
|
427
|
+
end)
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
def each_concurrently(options={}, &block)
|
432
|
+
Parallel.each(self.entries, options) { |val| block.call(val) }
|
433
|
+
end
|
434
|
+
|
435
|
+
def cycle
|
436
|
+
Sequence.new(Sequence::Generator.new do |g|
|
437
|
+
self.entries.cycle.each { |i| g.yield i }
|
438
|
+
end)
|
439
|
+
end
|
440
|
+
|
441
|
+
protected
|
302
442
|
|
303
|
-
private
|
304
443
|
def sequence
|
305
444
|
Sequence.new(self)
|
306
445
|
end
|
@@ -309,6 +448,63 @@ module Sequences
|
|
309
448
|
item.respond_to?(:empty?) ? item.empty? : !item
|
310
449
|
end
|
311
450
|
|
451
|
+
def serializer(container, entries)
|
452
|
+
entries.each do |entry|
|
453
|
+
if entry.is_a?(Sequences::Sequence)
|
454
|
+
data = []
|
455
|
+
serializer(data, entry)
|
456
|
+
container << {type: :sequence, values: data}
|
457
|
+
elsif entry.is_a?(Pair::Pair)
|
458
|
+
if entry.second.is_a?(Sequences::Sequence)
|
459
|
+
data = []
|
460
|
+
serializer(data, entry)
|
461
|
+
container << {type: :pair, values: data}
|
462
|
+
else
|
463
|
+
container << {type: :pair, values: entry.to_map}
|
464
|
+
end
|
465
|
+
elsif entry.is_a?(Option::Some)
|
466
|
+
container << {type: :some, values: entry.value}
|
467
|
+
elsif entry.is_a?(Option::None)
|
468
|
+
container << {type: :none, values: nil}
|
469
|
+
elsif entry.respond_to?(:each)
|
470
|
+
data = []
|
471
|
+
serializer(data, entry)
|
472
|
+
container << {type: entry.class, values: data}
|
473
|
+
else
|
474
|
+
container << entry
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
def deserializer(container, data)
|
480
|
+
data.each do |entry|
|
481
|
+
if entry.is_a?(Hash)
|
482
|
+
if entry[:type] == :sequence
|
483
|
+
data = []
|
484
|
+
deserializer(data, entry[:values])
|
485
|
+
container << Sequence.new(data)
|
486
|
+
elsif entry[:type] == :pair
|
487
|
+
if entry[:values].is_a?(Array)
|
488
|
+
container << pair(entry[:values].first, Sequence.new(entry[:values][1][:values]))
|
489
|
+
else
|
490
|
+
container << pair(entry[:values].keys.first, entry[:values].values.first)
|
491
|
+
end
|
492
|
+
elsif entry[:type] == :some
|
493
|
+
container << some(entry[:values])
|
494
|
+
elsif entry[:type] == :none
|
495
|
+
container << none
|
496
|
+
elsif entry[:type] == Hash
|
497
|
+
h = entry[:values].map { |e| [e[:values]].to_h }.reduce({}) { |a, b| a.merge(b) }
|
498
|
+
container << h
|
499
|
+
else
|
500
|
+
container << entry[:type].send(:new, entry[:values])
|
501
|
+
end
|
502
|
+
else
|
503
|
+
container << entry
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
312
508
|
end
|
313
509
|
|
314
510
|
class Empty < Sequence
|
data/lib/totally_lazy.rb
CHANGED
@@ -1,15 +1,138 @@
|
|
1
1
|
require_relative 'type_check'
|
2
|
+
require 'set'
|
3
|
+
require 'prime'
|
2
4
|
require_relative 'sequence'
|
3
5
|
require_relative 'pair'
|
4
6
|
require_relative 'option'
|
5
7
|
require_relative 'functor'
|
6
|
-
require_relative 'predicates'
|
8
|
+
require_relative 'predicates/predicates'
|
9
|
+
require_relative 'predicates/compare'
|
10
|
+
require_relative 'predicates/conversions'
|
11
|
+
require_relative 'predicates/numbers'
|
12
|
+
require_relative 'predicates/where'
|
13
|
+
require_relative 'predicates/where_processor'
|
14
|
+
require_relative 'parallel/parallel'
|
15
|
+
require_relative 'generators'
|
16
|
+
require_relative 'any'
|
7
17
|
|
8
18
|
include Sequences
|
9
19
|
include Option
|
10
20
|
include Pair
|
21
|
+
include Predicates
|
11
22
|
include Predicates::Numbers
|
12
23
|
include Predicates::Conversions
|
24
|
+
include Predicates::Compare
|
25
|
+
include Predicates::Where
|
26
|
+
include Generators
|
27
|
+
include Any
|
28
|
+
|
29
|
+
|
30
|
+
# p sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).filter(
|
31
|
+
# where( is(greater_than 2).or(is equal_to 5) )
|
32
|
+
# ).entries
|
33
|
+
#
|
34
|
+
# sequence.filter(where.is(greater_than 2))
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
# p sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).filter(where(is greater_than 2).or(is less_than 8 ).and(is odd)).entries
|
39
|
+
|
40
|
+
# p sequence(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).filter(where(is greater_than 2).and(is less_than 8 ).and(is even)).entries
|
41
|
+
|
42
|
+
# p sequence(pair(1, 2), pair(3, 4)).filter(where(key:odd)).entries
|
43
|
+
|
44
|
+
# p sequence(1,2,3,4,5,6).map_concurrently(as_string,in_threads:10).entries
|
45
|
+
# p sequence(1,2,3,4,5,6).map_concurrently{|i| i+5 }.entries
|
46
|
+
|
47
|
+
# sequence(1,2,3,4,5).each_concurrently{|i| sleep rand(10); p i }
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
# p Seq.repeat('car').take(10).entries
|
52
|
+
# p Seq.range(1,1000000000000000000000000000).take(20).entries
|
53
|
+
|
54
|
+
# p Seq.iterate(:*,2).take(10).entries
|
55
|
+
# p Seq.fibonacci.take(20).entries
|
56
|
+
# p Iter.fibonacci.take(20).entries
|
57
|
+
# p Iter.primes.take(10).entries
|
58
|
+
|
59
|
+
# p Seq.powers_of(3).take(10).entries
|
60
|
+
# p Iter.powers_of(3).take(10).entries
|
61
|
+
|
62
|
+
# p Seq.iterate(:+,1,0).take(10).entries
|
63
|
+
|
64
|
+
# p Seq.range(1,4).cycle.take(20).entries
|
65
|
+
# p Iter.range(1,4).cycle.take(20).entries
|
66
|
+
|
67
|
+
# p Seq.iterate(:+, 1).filter(even).take(2).reduce(:+)
|
68
|
+
|
69
|
+
#
|
70
|
+
# class Person
|
71
|
+
# attr_accessor :first_name,:last_name,:age
|
72
|
+
# def initialize(first_name,last_name,age)
|
73
|
+
# @first_name = first_name
|
74
|
+
# @last_name = last_name
|
75
|
+
# @age = age
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# def name
|
79
|
+
# @first_name
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# people = Seq.repeat(-> {Person.new(Any.string,Any.string,Any.string)}).take(10)
|
85
|
+
#
|
86
|
+
# p people.filter(where(first_name:matches(/s/))).map(&:first_name).entries
|
87
|
+
|
88
|
+
# p sequence(pair('apples','pairs'),pair('banana','orange'),pair('apples','melon')).filter(where key:matches(/app/)).entries
|
89
|
+
|
90
|
+
# p Seq.repeat(-> { rand(9)}).take(10).to_a.join.to_i
|
91
|
+
|
92
|
+
# p Seq.range(1,100000000000000).shuffle.take(10).to_a
|
93
|
+
|
94
|
+
# a = sequence(pair(7,8),sequence(1,2),sequence(3,sequence(5,6)),sequence(pair(:apple,99), option(1),none),[10,11],{:apple => 8,:pear => 9}).serialize
|
95
|
+
#
|
96
|
+
# p a
|
97
|
+
# p deserialize(a)[5]
|
98
|
+
|
99
|
+
# p sequence(pair(1,2))[0]
|
100
|
+
|
101
|
+
# p empty.gan(ser).entries
|
102
|
+
|
103
|
+
# def countdown(c,n)
|
104
|
+
# return if n.zero? # base case
|
105
|
+
# c << n
|
106
|
+
# countdown(c,n-1) # getting closer to base case
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
#
|
110
|
+
# a = []
|
111
|
+
# countdown(a,5)
|
112
|
+
# p a
|
113
|
+
|
114
|
+
|
115
|
+
# s1 = Seq.range(1,3000000)
|
116
|
+
# s2 = Seq.range(1,3000000)
|
117
|
+
#
|
118
|
+
# s = Time.now
|
119
|
+
# p s1.join2(s2).head
|
120
|
+
# f = Time.now
|
121
|
+
# p f - s
|
122
|
+
#
|
123
|
+
#
|
124
|
+
# s = Time.now
|
125
|
+
# p s1.join(s2).head
|
126
|
+
# f = Time.now
|
127
|
+
# p f - s
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
|
13
136
|
|
14
137
|
|
15
138
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe 'Functor' do
|
4
|
+
|
5
|
+
it 'should create a new functor' do
|
6
|
+
fc = Functor.new do |op, *a, &b|
|
7
|
+
[op, a, b]
|
8
|
+
end
|
9
|
+
expect(fc+1).to eq([:+, [1], nil])
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should support class method' do
|
13
|
+
fc = Functor.new do |op, *a, &b|
|
14
|
+
[op, a, b]
|
15
|
+
end
|
16
|
+
expect( fc.__class__).to eq(Functor)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should support to proc' do
|
20
|
+
f = Functor.new do |op, *a|
|
21
|
+
[op, *a]
|
22
|
+
end
|
23
|
+
p = f.to_proc
|
24
|
+
expect(p.class).to eq(Proc)
|
25
|
+
expect(p.call(:+,1,2,3)).to eq([:+,1,2,3])
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should cache a new functor' do
|
29
|
+
Functor.cache(:cached) do |op, *a, &b|
|
30
|
+
[op, a, b]
|
31
|
+
end
|
32
|
+
expect(Functor.cache(:cached) + 1).to eq([:+, [1], nil])
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe 'Generators' do
|
4
|
+
|
5
|
+
it 'should support repeat' do
|
6
|
+
expect(Seq.repeat('car').take(2)).to eq(sequence('car', 'car'))
|
7
|
+
expect(Iter.repeat('car').take(2).entries).to eq(%w(car car))
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should support range' do
|
11
|
+
expect(Seq.range(1, 4)).to eq(sequence(1, 2, 3, 4))
|
12
|
+
expect(Iter.range(1, 4).entries).to eq([1, 2, 3, 4])
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should support iterate' do
|
16
|
+
expect(Seq.iterate(:+, 1).take(4)).to eq(sequence(1, 2, 3, 4))
|
17
|
+
expect(Iter.iterate(:+, 1).take(4).entries).to eq([1, 2, 3, 4])
|
18
|
+
expect(Seq.iterate(:+, 1, 5).take(4)).to eq(sequence(5, 6, 7, 8))
|
19
|
+
expect(Iter.iterate(:+, 1, 5).take(4).entries).to eq([5, 6, 7, 8])
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should support primes' do
|
23
|
+
expect(Seq.primes.take(3)).to eq(sequence(2, 3, 5))
|
24
|
+
expect(Iter.primes.take(3).entries).to eq([2, 3, 5])
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should support fibonacci' do
|
28
|
+
expect(Seq.fibonacci.take(3)).to eq(sequence(1, 1, 2))
|
29
|
+
expect(Iter.fibonacci.take(3).entries).to eq([1, 1, 2])
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should support powers_of' do
|
33
|
+
expect(Seq.range(1, 4)).to eq(sequence(1, 2, 3, 4))
|
34
|
+
expect(Iter.range(1, 4).entries).to eq([1, 2, 3, 4])
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/spec/option_spec.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require_relative '../lib/totally_lazy'
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
3
2
|
|
4
3
|
describe 'Option' do
|
5
4
|
|
@@ -45,5 +44,77 @@ describe 'Option' do
|
|
45
44
|
expect { option(empty).get_or_throw(Exception) }.to raise_error(Exception)
|
46
45
|
end
|
47
46
|
|
47
|
+
it 'should return boolean for empty?' do
|
48
|
+
expect(option(1).empty?).to eq(false)
|
49
|
+
expect(option(empty).empty?).to eq(true)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should return boolean for defined?' do
|
53
|
+
expect(option(1).defined?).to eq(true)
|
54
|
+
expect(option('').defined?).to eq(false)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should convert to sequence' do
|
58
|
+
expect(option(1).to_seq).to eq(sequence(option(1)))
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should raise empty exception when calling get on none' do
|
62
|
+
expect { option(empty).get }.to raise_error(NoSuchElementException)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should raise empty exception when calling value on none' do
|
66
|
+
expect { option(empty).value }.to raise_error(NoSuchElementException)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should return true for empty on none' do
|
70
|
+
expect(option(empty).empty?).to eq(true)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should return false for defined on none' do
|
74
|
+
expect(option(empty).defined?).to eq(false)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should return the else in get_or_else for none' do
|
78
|
+
expect(option(empty).get_or_else(1)).to eq(1)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should return nil in get_or_nil for none' do
|
82
|
+
expect(option(empty).get_or_nil).to eq(nil)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should raise exception in get_or_throw for none' do
|
86
|
+
expect { option(empty).get_or_throw(Exception, 'oops') }.to raise_error(Exception)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should return sequence for none' do
|
90
|
+
expect(option(empty).to_seq).to eq(sequence(none))
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should return false for contains for none' do
|
94
|
+
expect(option(empty).contains('ooo')).to eq(false)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should return false for exists for none' do
|
98
|
+
pending('needs work')
|
99
|
+
pass
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should return false for contains for none' do
|
103
|
+
expect(option(empty).contains('ooo')).to eq(false)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should return original sequence for join on none' do
|
107
|
+
expect(option(empty).join(sequence(1,2))).to eq(sequence(1,2))
|
108
|
+
expect(option(empty).join([1,2])).to eq(sequence([1,2]))
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should know the type' do
|
112
|
+
expect(option(empty).is_none?).to be(true)
|
113
|
+
expect(option(empty).is_some?).to be(false)
|
114
|
+
expect(option(1).is_none?).to be(false)
|
115
|
+
expect(option(1).is_some?).to be(true)
|
116
|
+
end
|
117
|
+
|
118
|
+
|
48
119
|
|
49
120
|
end
|
data/spec/pair_spec.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require_relative '../lib/totally_lazy'
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
3
2
|
|
4
3
|
describe 'Pair' do
|
5
4
|
|
@@ -37,4 +36,8 @@ describe 'Pair' do
|
|
37
36
|
expect(pair('1', '2').to_f).to eq({1.0 => 2.0})
|
38
37
|
end
|
39
38
|
|
39
|
+
it 'should support each' do
|
40
|
+
expect(pair(1,2).each{|x| x }).to eq([1,2])
|
41
|
+
end
|
42
|
+
|
40
43
|
end
|