totally_lazy 0.1.22 → 0.1.23

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YWZiZTk0N2M1NTg1MjUxNjY2ODNlNWJhMDU4NjkyYTQzMzUwZTQ5Ng==
4
+ ZjQ0ZWZiYjdhYjFkODdjOTU3M2IzMTQ0MGY0YmYxMzUwMjI0OWQ0Ng==
5
5
  data.tar.gz: !binary |-
6
- Nzc1ZjRlMjM0YzYxZTcyNzcxZGIxNmNlODE3MjkxN2ZkMmZjZDJhOQ==
6
+ Mzg2NzRhNzgwZDYzODE4M2M4ZmY4OTZlOWRkMjViNDNlY2M2NjA4OQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MDAyYTdjNDMwNjU0MmIwOGVkYjZkNjA5ODdjNWRkODQzZjJjNmViYzM3OTAy
10
- NDEyMTgzYTc4YTJmZDYxMDdlYzg2MjBiNTMyNGZkNTJkMjhiOGEwNGI1Yzc4
11
- NTliZTZjNzkxZTQ0N2U2MTk1NmNlZGY4N2MxYjY2MTQ0MGRmNGU=
9
+ Y2M0MWVjYTEzOWFmZWI4MWQzNjFiMWI5MjkzNjY0NjkyYzMwYmE3MzM2YjNk
10
+ ZTgyOGU1MGY5NGQwNzk5ZTViMzFlZjc0NDE4YTIwNDg3ZjgwMjNiNzQwYTU5
11
+ NTc5NDBiNjM3MTdhZDM2MzQ2ODA4YzdjOWMyZDMwOWJkNTM0MGY=
12
12
  data.tar.gz: !binary |-
13
- NjlhNjBmNDQ5Yjg4MzQzNTI5OTIwNjBlZTdmYWNmNzQ2YjQyY2MwMGU3Njk5
14
- NDY4NjhhMDJiNWQzYTg3MTdmZTFhNGI4NWY1MWFiY2E5ZjQ0MmU0NmM3OGRl
15
- N2M0MjdjYzBjZmYwODU4OTY1ZDQ3NDFjYzQzYzM2YjFkYjY3YWE=
13
+ ZjE3ZTJmMzg1NzViZjY0ZGNiMmJlNGU2MDIwZjY4MDJlY2NjYjBkM2U4YjNh
14
+ MDQ2ZWU5YmQ1ZjkwYTEzNDYzYWE0YjQ5MzYxZDdjNGZmMWJmZmU5NzdkMDNl
15
+ NGI1YjQ5YWIxMDZmYWNlMzFhYjdjZGY4ZGM0MTBjZDQwYzMyNmI=
data/Rakefile CHANGED
@@ -36,7 +36,7 @@ Jeweler::Tasks.new do |gem|
36
36
  gem.description = 'Port of java functional library totallylazy to ruby'
37
37
  gem.email = 'rbarlow@raymanoz.com'
38
38
  gem.authors = ['Raymond Barlow', 'Kingsley Hendrickse']
39
- gem.required_ruby_version = '2.0.0'
39
+ gem.required_ruby_version = '>= 2.0.0'
40
40
  end
41
41
  Jeweler::RubygemsDotOrgTasks.new
42
42
 
data/lib/totally_lazy.rb CHANGED
@@ -1,19 +1,19 @@
1
- require_relative 'comparators'
2
- require_relative 'enumerators'
3
- require_relative 'functions'
4
- require_relative 'numbers'
5
- require_relative 'option'
6
- require_relative 'pair'
7
- require_relative 'predicates'
8
- require_relative 'sequence'
9
- require_relative 'strings'
1
+ require_relative 'totally_lazy/comparators'
2
+ require_relative 'totally_lazy/enumerators'
3
+ require_relative 'totally_lazy/functions'
4
+ require_relative 'totally_lazy/numbers'
5
+ require_relative 'totally_lazy/option'
6
+ require_relative 'totally_lazy/pair'
7
+ require_relative 'totally_lazy/predicates'
8
+ require_relative 'totally_lazy/sequence'
9
+ require_relative 'totally_lazy/strings'
10
10
 
11
11
  include Comparators
12
12
  include Enumerators
13
13
  include Functions
14
14
  include Numbers
15
- include Option
16
- include Pair
15
+ include Options
16
+ include Pairs
17
17
  include Predicates
18
18
  include Sequences
19
19
  include Strings
@@ -1,4 +1,5 @@
1
1
  module Comparators
2
+ private
2
3
  def ascending
3
4
  -> (a,b) { a <=> b }
4
5
  end
@@ -1,5 +1,6 @@
1
1
  module Enumerators
2
- def reverse(e)
2
+ private
3
+ def reverse_enumerator(e)
3
4
  e.reverse_each
4
5
  end
5
6
 
@@ -12,7 +13,7 @@ module Enumerators
12
13
  end
13
14
  end
14
15
 
15
- def enumerator(fn, init)
16
+ def enumerator_of(fn, init)
16
17
  Enumerator.new do |y|
17
18
  value = init
18
19
  y << value
@@ -23,10 +24,6 @@ module Enumerators
23
24
  end.lazy
24
25
  end
25
26
 
26
- def repeat_enumerator(value)
27
- Enumerators.repeat_fn_enumerator(returns(value))
28
- end
29
-
30
27
  def repeat_fn_enumerator(fn)
31
28
  Enumerator.new do |y|
32
29
  loop do
@@ -35,6 +32,10 @@ module Enumerators
35
32
  end.lazy
36
33
  end
37
34
 
35
+ def repeat_enumerator(value)
36
+ repeat_fn_enumerator(returns(value))
37
+ end
38
+
38
39
  def character_enumerator(string)
39
40
  Enumerator.new do |y|
40
41
  index = 0
@@ -1,19 +1,8 @@
1
1
  require 'concurrent/executors'
2
2
  require 'concurrent/promise'
3
3
 
4
- class Proc
5
- def optional
6
- ->(value) {
7
- begin
8
- option(self.(value))
9
- rescue
10
- none
11
- end
12
- }
13
- end
14
- end
15
-
16
4
  module Functions
5
+ private
17
6
  def monoid(fn, id)
18
7
  fn.define_singleton_method(:identity) do
19
8
  id
@@ -45,6 +34,8 @@ module Functions
45
34
  -> { raise e }
46
35
  end
47
36
 
37
+ alias call_throws call_raises
38
+
48
39
  def call
49
40
  ->(fn) { fn.() }
50
41
  end
@@ -1,4 +1,5 @@
1
1
  module Numbers
2
+ private
2
3
  def sum
3
4
  monoid(->(a, b) { a + b }, 0)
4
5
  end
@@ -28,7 +29,7 @@ module Numbers
28
29
  end
29
30
 
30
31
  def range_from(start)
31
- Sequence.new(Enumerators::enumerator(increment, start))
32
+ Sequence.new(enumerator_of(increment, start))
32
33
  end
33
34
 
34
35
  def range(from, to)
@@ -0,0 +1,191 @@
1
+ require_relative 'lambda_block'
2
+
3
+ class Proc
4
+ def optional
5
+ ->(value) {
6
+ begin
7
+ Option.option(self.(value))
8
+ rescue
9
+ Option.none
10
+ end
11
+ }
12
+ end
13
+ end
14
+
15
+ module Options
16
+ private
17
+ def option(value)
18
+ Option.option(value)
19
+ end
20
+
21
+ def some(value)
22
+ Option.some(value)
23
+ end
24
+
25
+ def none
26
+ Option.none
27
+ end
28
+ end
29
+
30
+ class Option
31
+ def self.option(value)
32
+ value.nil? ? none : some(value)
33
+ end
34
+
35
+ def self.some(value)
36
+ Some.new(value)
37
+ end
38
+
39
+ def self.none
40
+ NONE
41
+ end
42
+
43
+ def is_defined?
44
+ !is_empty?
45
+ end
46
+
47
+ def is?(fn_pred=nil, &block_pred)
48
+ assert_funcs(fn_pred, block_given?)
49
+ exists?(block_given? ? ->(value) { block_pred.call(value) } : fn_pred)
50
+ end
51
+
52
+ def flatten
53
+ flat_map(identity)
54
+ end
55
+ end
56
+
57
+ class Some < Option
58
+ include Comparable
59
+ include LambdaBlock
60
+
61
+ attr_reader :value
62
+
63
+ def initialize(value)
64
+ @value = value
65
+ end
66
+
67
+ def contains?(value)
68
+ @value == value
69
+ end
70
+
71
+ def exists?(fn_pred=nil, &block_pred)
72
+ assert_funcs(fn_pred, block_given?)
73
+ block_given? ? block_pred.call(@value) : fn_pred.(@value)
74
+ end
75
+
76
+ def map(fn=nil, &block)
77
+ assert_funcs(fn, block_given?)
78
+ option(block_given? ? block.call(@value) : fn.(@value))
79
+ end
80
+
81
+ def flat_map(fn=nil, &block) # function should return an option
82
+ assert_funcs(fn, block_given?)
83
+ block_given? ? block.call(@value) : fn.(@value)
84
+ end
85
+
86
+ def fold(seed, fn=nil, &block)
87
+ assert_funcs(fn, block_given?)
88
+ block_given? ? block.call(seed, @value) : fn.(seed, @value)
89
+ end
90
+
91
+ alias fold_left fold
92
+
93
+ def is_empty?
94
+ false
95
+ end
96
+
97
+ def size
98
+ 1
99
+ end
100
+
101
+ def get
102
+ @value
103
+ end
104
+
105
+ def get_or_else(value_or_fn=nil, &block)
106
+ assert_funcs(value_or_fn, block_given?)
107
+ get
108
+ end
109
+
110
+ alias or_else get_or_else
111
+
112
+ def enumerator
113
+ Enumerator.new { |y|
114
+ y << @value
115
+ raise StopIteration.new
116
+ }
117
+ end
118
+
119
+ def <=>(other)
120
+ @value <=> other.value
121
+ end
122
+
123
+ def to_s
124
+ "Some(#{value})"
125
+ end
126
+ end
127
+
128
+ class None < Option
129
+ include LambdaBlock
130
+
131
+ def is_empty?
132
+ true
133
+ end
134
+
135
+ def contains?(value)
136
+ false
137
+ end
138
+
139
+ def exists?(fn_pred=nil, &block_pred)
140
+ assert_funcs(fn_pred, block_given?)
141
+ false
142
+ end
143
+
144
+ def map(fn=nil, &block)
145
+ assert_funcs(fn, block_given?)
146
+ none
147
+ end
148
+
149
+ def flat_map(fn=nil, &block) # function should return an option
150
+ assert_funcs(fn, block_given?)
151
+ none
152
+ end
153
+
154
+ def fold(seed, fn=nil &block)
155
+ assert_funcs(fn, block_given?)
156
+ seed
157
+ end
158
+
159
+ alias fold_left fold
160
+
161
+ def size
162
+ 0
163
+ end
164
+
165
+ def get
166
+ raise NoSuchElementException.new
167
+ end
168
+
169
+ def get_or_else(value_or_fn=nil, &block)
170
+ assert_funcs(value_or_fn, block_given?)
171
+ if (value_or_fn.respond_to? :call) || block_given?
172
+ block_given? ? block.call : value_or_fn.()
173
+ else
174
+ value_or_fn
175
+ end
176
+ end
177
+
178
+ alias or_else get_or_else
179
+
180
+ def enumerator
181
+ Enumerator.new { |y|
182
+ raise StopIteration.new
183
+ }
184
+ end
185
+
186
+ def to_s
187
+ 'None'
188
+ end
189
+ end
190
+
191
+ NONE=None.new
@@ -0,0 +1,39 @@
1
+ module Pairs
2
+ private
3
+ def pair(first, second)
4
+ Pair.new(first, second)
5
+ end
6
+ end
7
+
8
+ class Pair
9
+ include Comparable
10
+
11
+ def initialize(first, second)
12
+ @first = -> { first }
13
+ @second = -> { second }
14
+ end
15
+
16
+ def first
17
+ @first.()
18
+ end
19
+
20
+ def second
21
+ @second.()
22
+ end
23
+
24
+ def enumerator
25
+ Enumerator.new { |y|
26
+ y << first
27
+ y << second
28
+ raise StopIteration.new
29
+ }
30
+ end
31
+
32
+ def <=>(other)
33
+ (first <=> other.first) <=> (second <=> other.second)
34
+ end
35
+
36
+ def to_s
37
+ "(#{first}, #{second})"
38
+ end
39
+ end
@@ -1,5 +1,6 @@
1
1
  module Predicates
2
- def not(pred)
2
+ private
3
+ def _not(pred)
3
4
  -> (bool) { !pred.(bool) }
4
5
  end
5
6
  end
@@ -0,0 +1,327 @@
1
+ require_relative 'lambda_block'
2
+
3
+ class NoSuchElementException < RuntimeError
4
+ end
5
+
6
+ module Sequences
7
+ def empty
8
+ Sequence.empty
9
+ end
10
+
11
+ def sequence(*items)
12
+ Sequence.sequence(*items)
13
+ end
14
+
15
+ def drop(sequence, count)
16
+ Sequence.drop(sequence, count)
17
+ end
18
+
19
+ def sort(sequence, comparator=ascending)
20
+ Sequence.sort(sequence, comparator)
21
+ end
22
+
23
+ def map_concurrently(sequence, fn=nil, &block)
24
+ Sequence.map_concurrently(sequence, block_given? ? ->(value) { block.call(value) } : fn)
25
+ end
26
+
27
+ def group(key, enumerator)
28
+ Group.new(key, enumerator)
29
+ end
30
+
31
+ def repeat(item)
32
+ Sequence.new(repeat_enumerator(item))
33
+ end
34
+
35
+ def repeat_fn(item)
36
+ Sequence.new(repeat_fn_enumerator(item))
37
+ end
38
+
39
+ private
40
+
41
+ def pair_enumerator(left, right)
42
+ Enumerator.new do |y|
43
+ left.rewind
44
+ right.rewind
45
+ loop do
46
+ y << pair(left.next, right.next)
47
+ end
48
+ end.lazy
49
+ end
50
+ end
51
+
52
+ class Sequence
53
+ include Comparable
54
+ include LambdaBlock
55
+
56
+ attr_reader :enumerator
57
+
58
+ def self.map_concurrently(sequence, fn=nil, &block)
59
+ call_concurrently(sequence.map(defer_return(block_given? ? ->(value) { block.call(value) } : fn)))
60
+ end
61
+
62
+ def self.sort(sequence, comparator=ascending)
63
+ Sequence.new(sequence.enumerator.sort { |a, b| comparator.(a, b) }.lazy)
64
+ end
65
+
66
+ def self.drop(sequence, count)
67
+ Sequence.new(sequence.enumerator.drop(count))
68
+ end
69
+
70
+ def self.repeat(item)
71
+ Sequence.new(repeat_enumerator(item))
72
+ end
73
+
74
+ def self.repeat_fn(item)
75
+ Sequence.new(repeat_fn_enumerator(item))
76
+ end
77
+
78
+ def self.take(sequence, count)
79
+ Sequence.new(sequence.enumerator.take(count))
80
+ end
81
+
82
+ def self.zip(left, right)
83
+ left.zip(right)
84
+ end
85
+
86
+
87
+ def self.sequence(*items)
88
+ if items.first.nil?
89
+ empty
90
+ else
91
+ Sequence.new(items.lazy)
92
+ end
93
+ end
94
+
95
+ def self.empty
96
+ EMPTY
97
+ end
98
+
99
+ def initialize(enumerator)
100
+ raise "Sequence only accepts Enumerator::Lazy, not #{enumerator.class}" unless (enumerator.class == Enumerator::Lazy)
101
+ @enumerator = enumerator
102
+ end
103
+
104
+ def is_empty?
105
+ @enumerator.rewind
106
+ begin
107
+ @enumerator.peek
108
+ false
109
+ rescue
110
+ true
111
+ end
112
+ end
113
+
114
+ def size
115
+ @enumerator.count
116
+ end
117
+
118
+ def head
119
+ @enumerator.first
120
+ end
121
+
122
+ alias first head
123
+
124
+ def second
125
+ tail.head
126
+ end
127
+
128
+ def head_option
129
+ option(head)
130
+ end
131
+
132
+ def last
133
+ reverse.head
134
+ end
135
+
136
+ def last_option
137
+ reverse.head_option
138
+ end
139
+
140
+ def reverse
141
+ Sequence.new(reverse_enumerator(@enumerator))
142
+ end
143
+
144
+ def tail
145
+ unless has_next(@enumerator)
146
+ raise NoSuchElementException.new
147
+ end
148
+ Sequence.new(@enumerator.drop(1))
149
+ end
150
+
151
+ def init
152
+ reverse.tail.reverse
153
+ end
154
+
155
+ def map(fn=nil, &block)
156
+ assert_funcs(fn, block_given?)
157
+ Sequence.new(@enumerator.map { |value|
158
+ block_given? ? block.call(value) : fn.(value)
159
+ })
160
+ end
161
+
162
+ def fold(seed, fn=nil, &block)
163
+ assert_funcs(fn, block_given?)
164
+ @enumerator.inject(seed) { |accumulator, value|
165
+ block_given? ? block.call(accumulator, value) : fn.(accumulator, value)
166
+ }
167
+ end
168
+
169
+ alias fold_left fold
170
+
171
+ def fold_right(seed, fn=nil, &block)
172
+ assert_funcs(fn, block_given?)
173
+ reverse_enumerator(@enumerator).inject(seed) { |accumulator, value|
174
+ block_given? ? block.call(value, accumulator) : fn.(value, accumulator)
175
+ }
176
+ end
177
+
178
+ def reduce(fn=nil, &block)
179
+ assert_funcs(fn, block_given?)
180
+ _fn = block_given? ? ->(a, b) { block.call(a, b) } : fn
181
+ accumulator = seed(@enumerator, fn)
182
+ while has_next(@enumerator)
183
+ accumulator = _fn.(accumulator, @enumerator.next)
184
+ end
185
+ accumulator
186
+ end
187
+
188
+ alias reduce_left reduce
189
+
190
+ def reduce_right(fn=nil, &block)
191
+ assert_funcs(fn, block_given?)
192
+ _fn = block_given? ? ->(a, b) { block.call(a, b) } : fn
193
+ reversed = reverse_enumerator(@enumerator)
194
+ accumulator = seed(reversed, fn)
195
+ while has_next(reversed)
196
+ accumulator = _fn.(reversed.next, accumulator)
197
+ end
198
+ accumulator
199
+ end
200
+
201
+ def find(fn_pred=nil, &block_pred)
202
+ assert_funcs(fn_pred, block_given?)
203
+ @enumerator.rewind
204
+ while has_next(@enumerator)
205
+ item = @enumerator.next
206
+ result = block_given? ? block_pred.call(item) : fn_pred.(item)
207
+ if result
208
+ return(some(item))
209
+ end
210
+ end
211
+ none
212
+ end
213
+
214
+ def zip(other)
215
+ Sequence.new(pair_enumerator(@enumerator, other.enumerator))
216
+ end
217
+
218
+ def zip_with_index
219
+ Sequence.zip(range_from(0), self)
220
+ end
221
+
222
+ def find_index_of(fn_pred=nil, &block_pred)
223
+ assert_funcs(fn_pred, block_given?)
224
+ zip_with_index.find(->(pair) { block_given? ? block_pred.call(pair.second) : fn_pred.(pair.second) }).map(->(pair) { pair.first })
225
+ end
226
+
227
+ def take(count)
228
+ Sequence.take(self, count)
229
+ end
230
+
231
+ def take_while(fn_pred=nil, &block_pred)
232
+ assert_funcs(fn_pred, block_given?)
233
+ Sequence.new(@enumerator.take_while { |value| block_given? ? block_pred.call(value) : fn_pred.(value) })
234
+ end
235
+
236
+ def drop(count)
237
+ Sequence.drop(self, count)
238
+ end
239
+
240
+ def drop_while(fn_pred=nil, &block_pred)
241
+ assert_funcs(fn_pred, block_given?)
242
+ Sequence.new(@enumerator.drop_while { |value| block_given? ? block_pred.call(value) : fn_pred.(value) })
243
+ end
244
+
245
+ def flat_map(fn=nil, &block)
246
+ assert_funcs(fn, block_given?)
247
+ map(block_given? ? ->(value) { block.call(value) } : fn).flatten
248
+ end
249
+
250
+ def flatten
251
+ Sequence.new(flatten_enumerator(enumerator))
252
+ end
253
+
254
+ def sort_by(comparator)
255
+ Sequence.sort(self, comparator)
256
+ end
257
+
258
+ def contains?(value)
259
+ @enumerator.member?(value)
260
+ end
261
+
262
+ def exists?(fn_pred=nil, &block_pred)
263
+ assert_funcs(fn_pred, block_given?)
264
+ @enumerator.any? { |value| block_given? ? block_pred.call(value) : fn_pred.(value) }
265
+ end
266
+
267
+ def for_all?(fn_pred=nil, &block_pred)
268
+ assert_funcs(fn_pred, block_given?)
269
+ @enumerator.all? { |value| block_given? ? block_pred.call(value) : fn_pred.(value) }
270
+ end
271
+
272
+ def filter(fn_pred=nil, &block_pred)
273
+ assert_funcs(fn_pred, block_given?)
274
+ Sequence.new(@enumerator.select { |value| block_given? ? block_pred.call(value) : fn_pred.(value) })
275
+ end
276
+
277
+ def reject(fn_pred=nil, &block_pred)
278
+ assert_funcs(fn_pred, block_given?)
279
+ filter(_not(block_given? ? ->(value) { block_pred.call(value) } : fn_pred))
280
+ end
281
+
282
+ def group_by(fn=nil, &block)
283
+ assert_funcs(fn, block_given?)
284
+ groups = @enumerator.group_by { |value| block_given? ? block.call(value) : fn.(value) }
285
+ Sequence.new(groups.to_a.map { |group| Group.new(group[0], group[1].lazy) }.lazy)
286
+ end
287
+
288
+ def each(fn=nil, &block)
289
+ assert_funcs(fn, block_given?)
290
+ @enumerator.each { |value| block_given? ? block.call(value) : fn.(value) }
291
+ end
292
+
293
+ def map_concurrently(fn=nil, &block)
294
+ assert_funcs(fn, block_given?)
295
+ Sequence.map_concurrently(self, block_given? ? ->(value) { block.call(value) } : fn)
296
+ end
297
+
298
+ def realise
299
+ Sequence.new(@enumerator.to_a.lazy)
300
+ end
301
+
302
+ def <=>(other)
303
+ @enumerator.entries <=> other.enumerator.entries
304
+ end
305
+
306
+ private
307
+ def seed(enumerator, fn)
308
+ enumerator.rewind
309
+ !fn.nil? && fn.respond_to?(:identity) ? fn.identity : enumerator.next
310
+ end
311
+ end
312
+
313
+ class Group < Sequence
314
+ include Comparable
315
+ attr_reader :key
316
+
317
+ def initialize(key, enumerator)
318
+ super(enumerator)
319
+ @key = key
320
+ end
321
+
322
+ def <=>(other)
323
+ (@key <=> other.key) <=> (enumerator.entries<=>(other.enumerator.entries))
324
+ end
325
+ end
326
+
327
+ EMPTY=Sequence.new([].lazy)
@@ -1,4 +1,5 @@
1
1
  module Strings
2
+ private
2
3
  def join(separator='')
3
4
  monoid(->(a, b) { "#{a}#{separator}#{b}" }, '')
4
5
  end
data/spec/option_spec.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require_relative 'spec_helper'
2
2
 
3
3
  describe 'Option' do
4
+
4
5
  it 'should support is_empty? & is_defined?' do
5
6
  expect(some(1).is_empty?).to eq(false)
6
7
  expect(some(1).is_defined?).to eq(true)
@@ -46,7 +47,7 @@ describe 'Option' do
46
47
 
47
48
  it 'should support flat_map' do
48
49
  expect(some(4).flat_map(divide(2).optional)).to eq(some(2))
49
- expect(some(4).flat_map{ |v| divide(2).optional.(v) }).to eq(some(2))
50
+ expect(some(4).flat_map { |v| divide(2).optional.(v) }).to eq(some(2))
50
51
  expect(some(4).flat_map(divide(0).optional)).to eq(none)
51
52
  expect(none.flat_map(constant(none))).to eq(none)
52
53
  expect(none.flat_map(some(4))).to eq(none)
@@ -65,16 +66,26 @@ describe 'Option' do
65
66
 
66
67
  it 'should support get' do
67
68
  expect(some(1).get).to eq(1)
68
- expect{none.get}.to raise_error(NoSuchElementException)
69
+ expect { none.get }.to raise_error(NoSuchElementException)
69
70
  end
70
71
 
71
- it 'should support get_or_else (aka or_else)' do
72
- expect(some(1).get_or_else(2)).to eq(1)
72
+ it 'should support get_or_else with value (aka or_else)' do
73
+ expect(Option::some(1).get_or_else(2)).to eq(1)
74
+ expect(some(1).or_else(2)).to eq(1)
73
75
  expect(none.get_or_else(2)).to eq(2)
74
76
  expect(option(1).get_or_else(2)).to eq(1)
75
77
  expect(option(nil).get_or_else(2)).to eq(2)
76
78
  end
77
79
 
80
+ it 'should support get_or_else with a function' do
81
+ expect(some(1).get_or_else(returns(2))).to eq(1)
82
+ expect(some(1).get_or_else { 2 }).to eq(1)
83
+ expect(none.get_or_else(returns(2))).to eq(2)
84
+ expect(option(1).get_or_else(returns(2))).to eq(1)
85
+ expect(option(nil).get_or_else(returns(2))).to eq(2)
86
+ expect { option(nil).get_or_else(call_raises(RuntimeError.new)) }.to raise_error(RuntimeError)
87
+ end
88
+
78
89
  it 'should raise exception if you try to use both lambda and block' do
79
90
  expect { some(1).exists?(->(a) { a == 1 }) { |b| b == 2 } }.to raise_error(RuntimeError)
80
91
  expect { none.exists?(->(a) { a == 1 }) { |b| b == 2 } }.to raise_error(RuntimeError)
@@ -86,7 +97,8 @@ describe 'Option' do
86
97
  expect { none.fold_left(0, ->(a, b) { a+b }) { |a, b| a+b } }.to raise_error(RuntimeError)
87
98
  expect { some(1).map(->(v) { v.to_s }) { |v| v.to_s } }.to raise_error(RuntimeError)
88
99
  expect { none.map(->(v) { v.to_s }) { |v| v.to_s } }.to raise_error(RuntimeError)
89
- expect { some(4).flat_map(divide(2).optional){ |v| divide(2).optional.(v) } }.to raise_error(RuntimeError)
100
+ expect { some(4).flat_map(divide(2).optional) { |v| divide(2).optional.(v) } }.to raise_error(RuntimeError)
101
+ expect { some(1).get_or_else(returns(2)) { |value| 3 } }.to raise_error(RuntimeError)
90
102
  end
91
103
 
92
104
  end
@@ -1,6 +1,7 @@
1
1
  require_relative 'spec_helper'
2
2
 
3
3
  describe 'Sequence' do
4
+
4
5
  it 'should create empty sequence when iterable is nil' do
5
6
  expect(sequence(nil)).to eq(empty)
6
7
  end
@@ -78,7 +79,7 @@ describe 'Sequence' do
78
79
  end
79
80
 
80
81
  it 'should support composite predicates' do
81
- expect(sequence(1, 2, 3, 4).filter(Predicates::not(even))).to eq(sequence(1, 3))
82
+ expect(sequence(1, 2, 3, 4).filter(_not(even))).to eq(sequence(1, 3))
82
83
  end
83
84
 
84
85
  it 'should support reject' do
@@ -187,8 +188,8 @@ describe 'Sequence' do
187
188
  end
188
189
 
189
190
  it 'should support repeat' do
190
- expect(Sequences.repeat(10).take(5)).to eq(sequence(10, 10, 10, 10, 10))
191
- expect(Sequences.repeat_fn(returns(20)).take(5)).to eq(sequence(20, 20, 20, 20, 20))
191
+ expect(repeat(10).take(5)).to eq(sequence(10, 10, 10, 10, 10))
192
+ expect(repeat_fn(returns(20)).take(5)).to eq(sequence(20, 20, 20, 20, 20))
192
193
  end
193
194
 
194
195
  it 'should support is_empty?' do
data/spec/spec_helper.rb CHANGED
@@ -2,7 +2,7 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
3
 
4
4
  require 'rspec'
5
- require 'totally_lazy'
5
+ require_relative '../lib/totally_lazy'
6
6
 
7
7
  # Requires supporting files with custom matchers and macros, etc,
8
8
  # in ./support/ and its subdirectories.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: totally_lazy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.22
4
+ version: 0.1.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raymond Barlow
@@ -154,17 +154,17 @@ files:
154
154
  - Rakefile
155
155
  - VERSION
156
156
  - contributors.txt
157
- - lib/comparators.rb
158
- - lib/enumerators.rb
159
- - lib/functions.rb
160
- - lib/lambda_block.rb
161
- - lib/numbers.rb
162
- - lib/option.rb
163
- - lib/pair.rb
164
- - lib/predicates.rb
165
- - lib/sequence.rb
166
- - lib/strings.rb
167
157
  - lib/totally_lazy.rb
158
+ - lib/totally_lazy/comparators.rb
159
+ - lib/totally_lazy/enumerators.rb
160
+ - lib/totally_lazy/functions.rb
161
+ - lib/totally_lazy/lambda_block.rb
162
+ - lib/totally_lazy/numbers.rb
163
+ - lib/totally_lazy/option.rb
164
+ - lib/totally_lazy/pair.rb
165
+ - lib/totally_lazy/predicates.rb
166
+ - lib/totally_lazy/sequence.rb
167
+ - lib/totally_lazy/strings.rb
168
168
  - readme.md
169
169
  - spec/option_spec.rb
170
170
  - spec/sequence_spec.rb
@@ -180,7 +180,7 @@ require_paths:
180
180
  - lib
181
181
  required_ruby_version: !ruby/object:Gem::Requirement
182
182
  requirements:
183
- - - '='
183
+ - - ! '>='
184
184
  - !ruby/object:Gem::Version
185
185
  version: 2.0.0
186
186
  required_rubygems_version: !ruby/object:Gem::Requirement
data/lib/option.rb DELETED
@@ -1,156 +0,0 @@
1
- require_relative 'lambda_block'
2
-
3
- module Option
4
- include LambdaBlock
5
-
6
- def option(value)
7
- value.nil? ? none : some(value)
8
- end
9
-
10
- def some(value)
11
- Some.new(value)
12
- end
13
-
14
- def none
15
- NONE
16
- end
17
-
18
- class Option
19
- def is_defined?
20
- !is_empty?
21
- end
22
- end
23
-
24
- def is?(fn_pred=nil, &block_pred)
25
- assert_funcs(fn_pred, block_given?)
26
- exists?(block_given? ? ->(value) { block_pred.call(value) } : fn_pred)
27
- end
28
-
29
- def flatten
30
- flat_map(identity)
31
- end
32
-
33
- class Some < Option
34
- include Comparable
35
- attr_reader :value
36
-
37
- def initialize(value)
38
- @value = value
39
- end
40
-
41
- def contains?(value)
42
- @value == value
43
- end
44
-
45
- def exists?(fn_pred=nil, &block_pred)
46
- assert_funcs(fn_pred, block_given?)
47
- block_given? ? block_pred.call(@value) : fn_pred.(@value)
48
- end
49
-
50
- def map(fn=nil, &block)
51
- assert_funcs(fn, block_given?)
52
- option(block_given? ? block.call(@value) : fn.(@value))
53
- end
54
-
55
- def flat_map(fn=nil, &block) # function should return an option
56
- assert_funcs(fn, block_given?)
57
- block_given? ? block.call(@value) : fn.(@value)
58
- end
59
-
60
- def fold(seed, fn=nil, &block)
61
- assert_funcs(fn, block_given?)
62
- block_given? ? block.call(seed, @value) : fn.(seed, @value)
63
- end
64
-
65
- alias fold_left fold
66
-
67
- def is_empty?
68
- false
69
- end
70
-
71
- def size
72
- 1
73
- end
74
-
75
- def get
76
- @value
77
- end
78
-
79
- def get_or_else(value)
80
- get
81
- end
82
-
83
- def enumerator
84
- Enumerator.new { |y|
85
- y << @value
86
- raise StopIteration.new
87
- }
88
- end
89
-
90
- def <=>(other)
91
- @value <=> other.value
92
- end
93
-
94
- def to_s
95
- "Some(#{value})"
96
- end
97
- end
98
-
99
- class None < Option
100
- def is_empty?
101
- true
102
- end
103
-
104
- def contains?(value)
105
- false
106
- end
107
-
108
- def exists?(fn_pred=nil, &block_pred)
109
- assert_funcs(fn_pred, block_given?)
110
- false
111
- end
112
-
113
- def map(fn=nil, &block)
114
- assert_funcs(fn, block_given?)
115
- none
116
- end
117
-
118
- def flat_map(fn=nil, &block) # function should return an option
119
- assert_funcs(fn, block_given?)
120
- none
121
- end
122
-
123
- def fold(seed, fn=nil &block)
124
- assert_funcs(fn, block_given?)
125
- seed
126
- end
127
-
128
- alias fold_left fold
129
-
130
- def size
131
- 0
132
- end
133
-
134
- def get
135
- raise NoSuchElementException.new
136
- end
137
-
138
- def get_or_else(value)
139
- value
140
- end
141
-
142
- alias or_else get_or_else
143
-
144
- def enumerator
145
- Enumerator.new { |y|
146
- raise StopIteration.new
147
- }
148
- end
149
-
150
- def to_s
151
- 'None'
152
- end
153
- end
154
-
155
- NONE=None.new
156
- end
data/lib/pair.rb DELETED
@@ -1,40 +0,0 @@
1
- module Pair
2
-
3
- def pair(first, second)
4
- Pair.new(first, second)
5
- end
6
-
7
- class Pair
8
- include Comparable
9
-
10
- def initialize(first, second)
11
- @first = -> { first }
12
- @second = -> { second }
13
- end
14
-
15
- def first
16
- @first.()
17
- end
18
-
19
- def second
20
- @second.()
21
- end
22
-
23
- def enumerator
24
- Enumerator.new { |y|
25
- y << first
26
- y << second
27
- raise StopIteration.new
28
- }
29
- end
30
-
31
- def <=>(other)
32
- (first <=> other.first) <=> (second <=> other.second)
33
- end
34
-
35
- def to_s
36
- "(#{first}, #{second})"
37
- end
38
- end
39
-
40
- end
data/lib/sequence.rb DELETED
@@ -1,298 +0,0 @@
1
- require_relative 'lambda_block'
2
-
3
- class NoSuchElementException < RuntimeError
4
- end
5
-
6
- module Sequences
7
- def empty
8
- EMPTY
9
- end
10
-
11
- def sequence(*items)
12
- if items.first.nil?
13
- empty
14
- else
15
- Sequence.new(items.lazy)
16
- end
17
- end
18
-
19
- def zip(left, right)
20
- left.zip(right)
21
- end
22
-
23
- def take(sequence, count)
24
- Sequence.new(sequence.enumerator.take(count))
25
- end
26
-
27
- def drop(sequence, count)
28
- Sequence.new(sequence.enumerator.drop(count))
29
- end
30
-
31
- def repeat(item)
32
- Sequence.new(repeat_enumerator(item))
33
- end
34
-
35
- def repeat_fn(item)
36
- Sequence.new(repeat_fn_enumerator(item))
37
- end
38
-
39
- def sort(sequence, comparator=ascending)
40
- Sequence.new(sequence.enumerator.sort { |a, b| comparator.(a, b) }.lazy)
41
- end
42
-
43
- def map_concurrently(sequence, fn=nil, &block)
44
- call_concurrently(sequence.map(defer_return(block_given? ? ->(value) { block.call(value) } : fn)))
45
- end
46
-
47
- # noinspection RubyTooManyMethodsInspection
48
- class Sequence
49
- include Comparable
50
- include LambdaBlock
51
- attr_reader :enumerator
52
-
53
- def initialize(enumerator)
54
- raise "Sequence only accepts Enumerator::Lazy, not #{enumerator.class}" unless (enumerator.class == Enumerator::Lazy)
55
- @enumerator = enumerator
56
- end
57
-
58
- def is_empty?
59
- @enumerator.rewind
60
- begin
61
- @enumerator.peek
62
- false
63
- rescue
64
- true
65
- end
66
- end
67
-
68
- def size
69
- @enumerator.count
70
- end
71
-
72
- def head
73
- @enumerator.first
74
- end
75
-
76
- alias first head
77
-
78
- def second
79
- tail.head
80
- end
81
-
82
- def head_option
83
- option(head)
84
- end
85
-
86
- def last
87
- reverse.head
88
- end
89
-
90
- def last_option
91
- reverse.head_option
92
- end
93
-
94
- def reverse
95
- Sequence.new(Enumerators::reverse(@enumerator))
96
- end
97
-
98
- def tail
99
- unless has_next(@enumerator)
100
- raise NoSuchElementException.new
101
- end
102
- Sequence.new(@enumerator.drop(1))
103
- end
104
-
105
- def init
106
- reverse.tail.reverse
107
- end
108
-
109
- def map(fn=nil, &block)
110
- assert_funcs(fn, block_given?)
111
- Sequence.new(@enumerator.map { |value|
112
- block_given? ? block.call(value) : fn.(value)
113
- })
114
- end
115
-
116
- def fold(seed, fn=nil, &block)
117
- assert_funcs(fn, block_given?)
118
- @enumerator.inject(seed) { |accumulator, value|
119
- block_given? ? block.call(accumulator, value) : fn.(accumulator, value)
120
- }
121
- end
122
-
123
- alias fold_left fold
124
-
125
- def fold_right(seed, fn=nil, &block)
126
- assert_funcs(fn, block_given?)
127
- Enumerators::reverse(@enumerator).inject(seed) { |accumulator, value|
128
- block_given? ? block.call(value, accumulator) : fn.(value, accumulator)
129
- }
130
- end
131
-
132
- def reduce(fn=nil, &block)
133
- assert_funcs(fn, block_given?)
134
- _fn = block_given? ? ->(a, b) { block.call(a, b) } : fn
135
- accumulator = seed(@enumerator, fn)
136
- while has_next(@enumerator)
137
- accumulator = _fn.(accumulator, @enumerator.next)
138
- end
139
- accumulator
140
- end
141
-
142
- alias reduce_left reduce
143
-
144
- def reduce_right(fn=nil, &block)
145
- assert_funcs(fn, block_given?)
146
- _fn = block_given? ? ->(a, b) { block.call(a, b) } : fn
147
- reversed = Enumerators::reverse(@enumerator)
148
- accumulator = seed(reversed, fn)
149
- while has_next(reversed)
150
- accumulator = _fn.(reversed.next, accumulator)
151
- end
152
- accumulator
153
- end
154
-
155
- def find(fn_pred=nil, &block_pred)
156
- assert_funcs(fn_pred, block_given?)
157
- @enumerator.rewind
158
- while has_next(@enumerator)
159
- item = @enumerator.next
160
- result = block_given? ? block_pred.call(item) : fn_pred.(item)
161
- if result
162
- return(some(item))
163
- end
164
- end
165
- none
166
- end
167
-
168
- def zip(other)
169
- Sequence.new(pair_enumerator(@enumerator, other.enumerator))
170
- end
171
-
172
- def zip_with_index
173
- Sequences.zip(range_from(0), self)
174
- end
175
-
176
- def find_index_of(fn_pred=nil, &block_pred)
177
- assert_funcs(fn_pred, block_given?)
178
- zip_with_index.find(->(pair) { block_given? ? block_pred.call(pair.second) : fn_pred.(pair.second) }).map(->(pair) { pair.first })
179
- end
180
-
181
- def take(count)
182
- Sequences::take(self, count)
183
- end
184
-
185
- def take_while(fn_pred=nil, &block_pred)
186
- assert_funcs(fn_pred, block_given?)
187
- Sequence.new(@enumerator.take_while { |value| block_given? ? block_pred.call(value) : fn_pred.(value) })
188
- end
189
-
190
- def drop(count)
191
- Sequences::drop(self, count)
192
- end
193
-
194
- def drop_while(fn_pred=nil, &block_pred)
195
- assert_funcs(fn_pred, block_given?)
196
- Sequence.new(@enumerator.drop_while { |value| block_given? ? block_pred.call(value) : fn_pred.(value) })
197
- end
198
-
199
- def flat_map(fn=nil, &block)
200
- assert_funcs(fn, block_given?)
201
- map(block_given? ? ->(value) { block.call(value) } : fn).flatten
202
- end
203
-
204
- def flatten
205
- Sequence.new(flatten_enumerator(enumerator))
206
- end
207
-
208
- def sort_by(comparator)
209
- Sequences::sort(self, comparator)
210
- end
211
-
212
- def contains?(value)
213
- @enumerator.member?(value)
214
- end
215
-
216
- def exists?(fn_pred=nil, &block_pred)
217
- assert_funcs(fn_pred, block_given?)
218
- @enumerator.any? { |value| block_given? ? block_pred.call(value) : fn_pred.(value) }
219
- end
220
-
221
- def for_all?(fn_pred=nil, &block_pred)
222
- assert_funcs(fn_pred, block_given?)
223
- @enumerator.all? { |value| block_given? ? block_pred.call(value) : fn_pred.(value) }
224
- end
225
-
226
- def filter(fn_pred=nil, &block_pred)
227
- assert_funcs(fn_pred, block_given?)
228
- Sequence.new(@enumerator.select { |value| block_given? ? block_pred.call(value) : fn_pred.(value) })
229
- end
230
-
231
- def reject(fn_pred=nil, &block_pred)
232
- assert_funcs(fn_pred, block_given?)
233
- filter(Predicates::not(block_given? ? ->(value) { block_pred.call(value) } : fn_pred))
234
- end
235
-
236
- def group_by(fn=nil, &block)
237
- assert_funcs(fn, block_given?)
238
- groups = @enumerator.group_by { |value| block_given? ? block.call(value) : fn.(value) }
239
- Sequence.new(groups.to_a.map { |group| Group.new(group[0], group[1].lazy) }.lazy)
240
- end
241
-
242
- def each(fn=nil, &block)
243
- assert_funcs(fn, block_given?)
244
- @enumerator.each { |value| block_given? ? block.call(value) : fn.(value) }
245
- end
246
-
247
- def map_concurrently(fn=nil, &block)
248
- assert_funcs(fn, block_given?)
249
- Sequences::map_concurrently(self, block_given? ? ->(value) { block.call(value) } : fn)
250
- end
251
-
252
- def realise
253
- Sequence.new(@enumerator.to_a.lazy)
254
- end
255
-
256
- def <=>(other)
257
- @enumerator.entries <=> other.enumerator.entries
258
- end
259
-
260
- private
261
- def seed(enumerator, fn)
262
- enumerator.rewind
263
- !fn.nil? && fn.respond_to?(:identity) ? fn.identity : enumerator.next
264
- end
265
- end
266
-
267
- def group(key, enumerator)
268
- Group.new(key, enumerator)
269
- end
270
-
271
- class Group < Sequence
272
- include Comparable
273
- attr_reader :key
274
-
275
- def initialize(key, enumerator)
276
- super(enumerator)
277
- @key = key
278
- end
279
-
280
- def <=>(other)
281
- (@key <=> other.key) <=> (enumerator.entries<=>(other.enumerator.entries))
282
- end
283
- end
284
-
285
- private
286
-
287
- EMPTY=Sequence.new([].lazy)
288
-
289
- def pair_enumerator(left, right)
290
- Enumerator.new do |y|
291
- left.rewind
292
- right.rewind
293
- loop do
294
- y << pair(left.next, right.next)
295
- end
296
- end.lazy
297
- end
298
- end