totally_lazy 0.0.4 → 0.0.5
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/.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
|