totally_lazy 0.1.22 → 0.1.23

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 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