totally_lazy 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d2bd08beb128d57daef7cb8259b9dd391d77d3f6
4
- data.tar.gz: 0436a18ccd3dc6f192af42e80fbfd8467e0c068b
3
+ metadata.gz: 580d257ae0a5c6db629420f25fc435870cbd09d7
4
+ data.tar.gz: 428a631231457eb51abd105a07a91b8ad5517b26
5
5
  SHA512:
6
- metadata.gz: 562656c8ed06737fedb4dd348d788e484786873855c0ecd5fb951c651ed2383a90a738c2cd64f444155589b07df9c08881964e0fafbaeebdffd11d4200d47bce
7
- data.tar.gz: 3c77e9a13c67a68b76797583a83179259ffca73e5a754902b31e99c51c8c2874711d07a3943836669dea4e7025d2147090e0f69672dc3f46d8589850f339ad4b
6
+ metadata.gz: 831ab147b15383e7fc2ff86c4d85636cc3a93ae14c64efa2b0a4a2cf06c0cb89ff6f43ffe747273252f595213f4c0e13c5943646e4b2e786c2095272a428de4d
7
+ data.tar.gz: aeff00bdcac329c9554adca869f9c022d7c9e11eef72958ae61e434a0a590dcff45b14a9aad03ce0af3ed15d685b2718c0ce10b992420e9886ed747a6e0bc6d5
data/Gemfile CHANGED
@@ -6,4 +6,5 @@ group :development do
6
6
  gem 'bundler', '~> 1.0'
7
7
  gem 'jeweler', '~> 2.0.1'
8
8
  gem 'rspec_html_formatter', '~> 0.3.0'
9
+ gem 'rake', '~> 10.3.2'
9
10
  end
data/README.md CHANGED
@@ -13,7 +13,7 @@ This is a port of the java functional library [Totally Lazy](https://code.google
13
13
  In your bundler Gemfile
14
14
 
15
15
  ```ruby
16
- gem totally_lazy, '~>0.0.2'
16
+ gem totally_lazy, '~>0.0.3'
17
17
  ```
18
18
 
19
19
  Or with rubygems
@@ -27,8 +27,10 @@ Or with rubygems
27
27
  The following are some simple examples of the currently implemented functionality.
28
28
 
29
29
  ```ruby
30
- sequence(1,2,3,4).filter{|i| i.even? } # lazily returns 2,4
31
- sequence(1,2).map{|i| i.to_s} # lazily returns "1","2"
30
+ require 'totally_lazy'
31
+
32
+ sequence(1,2,3,4).filter(even) # lazily returns 2,4
33
+ sequence(1,2).map(to_string) # lazily returns "1","2"
32
34
  sequence(1,2,3).take(2) # lazily returns 1,2
33
35
  sequence(1,2,3).drop(2) # lazily returns 3
34
36
  sequence(1,2,3).tail # lazily returns 2,3
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
data/lib/functor.rb ADDED
@@ -0,0 +1,92 @@
1
+ # Functor is Ruby's implementation of a Higher-Order-Message. Essentally,
2
+ # a Functor can vary its behavior accorrding to the operation applied to it.
3
+ #
4
+ # Example
5
+ #
6
+ # f = Functor.new { |op, x| x.send(op, x) }
7
+ # (f + 1) #=> 2
8
+ # (f + 2) #=> 4
9
+ # (f + 3) #=> 6
10
+ # (f * 1) #=> 1
11
+ # (f * 2) #=> 4
12
+ # (f * 3) #=> 9
13
+ #
14
+ class Functor #< BasicObject
15
+
16
+ # Functors can be somewhat inefficient if a new Functor
17
+ # is frequently recreated for the same use. So this cache
18
+ # can be used to speed things up.
19
+ #
20
+ # The +key+ will always be an array, wich makes it easier
21
+ # to cache Functor for multiple factors.
22
+ #
23
+ def self.cache(*key, &function)
24
+ @cache ||= {}
25
+ if function
26
+ @cache[key] = new(&function)
27
+ else
28
+ @cache[key]
29
+ end
30
+ end
31
+
32
+ EXCEPTIONS = [:binding, :inspect, :object_id]
33
+ if defined?(::BasicObject)
34
+ EXCEPTIONS.concat(::BasicObject.instance_methods)
35
+ EXCEPTIONS.uniq!
36
+ EXCEPTIONS.map!{ |m| m.to_sym }
37
+ end
38
+
39
+ #
40
+ alias :__class__ :class
41
+
42
+ ## If Functor were built-in to Ruby this would not be
43
+ ## needed since exceptions could just be added directly.
44
+ ##$FUNCTOR_EXCEPTIONS ||= [:binding, :undefine_method]
45
+
46
+ ## TODO: This will not work when BasicObject is utilized. How to fix?
47
+ ##def self.honor_exceptions
48
+ ## $FUNCTOR_EXCEPTIONS.each{ |name|
49
+ ## next if method_defined?(name)
50
+ ## eval %{
51
+ ## def #{name}(*a,&b)
52
+ ## super(*a, &b)
53
+ ## end
54
+ ## }
55
+ ## }
56
+ ##end
57
+
58
+ # Privatize all methods except vital methods and #binding.
59
+ instance_methods(true).each do |m|
60
+ next if m.to_s =~ /^__/
61
+ next if EXCEPTIONS.include?(m.to_sym)
62
+ undef_method(m)
63
+ end
64
+
65
+ #
66
+ def initialize(&function)
67
+ @function = function
68
+ end
69
+
70
+ #
71
+ def to_proc
72
+ @function
73
+ end
74
+
75
+ ##def inspect
76
+ ## #"#<#{__class__}:#{object_id} #{method_missing(:inspect)}>" # hex id ?
77
+ ## "#{method_missing(:inspect)}"
78
+ ##end
79
+
80
+ ## Needed?
81
+ ##def send(op, *a, &b)
82
+ ## method_missing(op, *a, &b)
83
+ ##end
84
+
85
+ private
86
+
87
+ # Any action against the Functor is processesd by the function.
88
+ def method_missing(op, *args, &blk)
89
+ @function.call(op, *args, &blk)
90
+ end
91
+
92
+ end
data/lib/predicates.rb ADDED
@@ -0,0 +1,35 @@
1
+ module Predicates
2
+
3
+ module Numbers
4
+
5
+ def even
6
+ -> (v) { Type.responds(v, :even?); v if v.even? }
7
+ end
8
+
9
+ def odd
10
+ -> (v) { Type.responds(v, :odd?); v if v.odd? }
11
+ end
12
+
13
+ end
14
+
15
+ module Conversions
16
+
17
+ def to_string
18
+ -> (v) { v.to_s }
19
+ end
20
+
21
+ def to_int
22
+ -> (v) { Type.responds(v, :to_i); v.to_i }
23
+ end
24
+
25
+ def to_float
26
+ -> (v) { Type.responds(v, :to_i); v.to_f }
27
+ end
28
+
29
+ def to_array
30
+ -> (v) { [v] }
31
+ end
32
+
33
+ end
34
+
35
+ end
data/lib/sequence.rb CHANGED
@@ -1,22 +1,17 @@
1
1
  class NoSuchElementException < RuntimeError
2
2
  end
3
3
 
4
- class Empty
5
-
6
- def each
7
- yield self
8
- end
9
-
4
+ class UnsupportedTypeException < RuntimeError
10
5
  end
11
6
 
12
7
  module Sequences
13
8
 
14
9
  def sequence(*items)
15
10
  if items.size == 1
16
- if items.first.kind_of?(Range)
17
- Sequence.new(items.first)
18
- elsif items.first.kind_of?(Hash)
11
+ if [Range, Hash].include?(items.first.class)
19
12
  Sequence.new(items.first)
13
+ elsif items.first.nil?
14
+ empty
20
15
  else
21
16
  Sequence.new(items)
22
17
  end
@@ -26,7 +21,7 @@ module Sequences
26
21
  end
27
22
 
28
23
  def empty
29
- sequence(Empty.new)
24
+ Empty.new
30
25
  end
31
26
 
32
27
  class Sequence < Enumerator
@@ -49,23 +44,37 @@ module Sequences
49
44
  end
50
45
 
51
46
  def <=>(object)
52
- self.class <=> object.class
47
+ self.entries <=> object.entries
53
48
  end
54
49
 
55
- def map(&block)
56
- Sequence.new(self) { |yielder, val|
57
- yielder << block.call(val)
58
- }
50
+ def map(predicate=nil, &block)
51
+ if predicate
52
+ Sequence.new(self) { |yielder, val|
53
+ v = predicate.call(val)
54
+ yielder << v unless v.nil?
55
+ }
56
+ else
57
+ Sequence.new(self) { |yielder, val|
58
+ yielder << block.call(val)
59
+ }
60
+ end
59
61
  end
60
62
 
61
63
  alias collect map
62
64
 
63
- def select(&block)
64
- Sequence.new(self) { |yielder, val|
65
- if block.call(val)
66
- yielder << val
67
- end
68
- }
65
+ def select(predicate=nil, &block)
66
+ if predicate
67
+ Sequence.new(self) { |yielder, val|
68
+ v = predicate.call(val)
69
+ yielder << v unless v.nil?
70
+ }
71
+ else
72
+ Sequence.new(self) { |yielder, val|
73
+ if block.call(val)
74
+ yielder << val
75
+ end
76
+ }
77
+ end
69
78
  end
70
79
 
71
80
  alias find_all select
@@ -165,7 +174,11 @@ module Sequences
165
174
  alias get []
166
175
 
167
176
  def empty?
168
- sequence.next.kind_of?(Empty)
177
+ begin
178
+ sequence.peek_values.empty?
179
+ rescue StopIteration
180
+ true
181
+ end
169
182
  end
170
183
 
171
184
  def head
@@ -206,30 +219,115 @@ module Sequences
206
219
  def transpose
207
220
  Sequence.new(Sequence::Generator.new do |g|
208
221
  result = []
209
- max_size = self.entries.max { |a, b| a.size <=> b.size }.size
222
+ max_size = self.to_a.max { |a, b| a.size <=> b.size }.size
210
223
  max_size.times do |i|
211
- result[i] = [self.entries.first.size]
212
- self.entries.each_with_index { |r, j| result[i][j] = r[i] }
224
+ result[i] = [self.to_a.first.size]
225
+ self.to_a.each_with_index { |r, j| result[i][j] = r[i] }
213
226
  end
214
227
  result
215
- self.empty? ? raise(NoSuchElementException.new, 'The sequence was empty') : result.each { |i| g.yield i }
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
+ result.each { |i| g.yield i }
216
234
  end)
217
235
  end
218
236
 
219
237
  def join(target_sequence)
220
238
  Sequence.new(Sequence::Generator.new do |g|
221
239
  raise(Exception.new, 'The target (right side) must be a sequence') unless target_sequence.kind_of?(Sequences::Sequence)
222
- (self.entries << target_sequence.entries).flatten.each{|i| g.yield i}
240
+ elements = self.entries
241
+ elements = elements.reject { |i| i.is_a?(Empty) }
242
+ (elements << target_sequence.entries).flatten.each { |i| g.yield i }
223
243
  end)
224
244
  end
225
- alias + join
245
+
226
246
  alias << join
227
247
 
248
+ def add(target_sequence)
249
+ Sequence.new(Sequence::Generator.new do |g|
250
+ (self.entries + target_sequence.entries).each { |i| g.yield i }
251
+ end)
252
+ end
253
+
254
+ alias + add
255
+
256
+ def append(item)
257
+ Sequence.new(Sequence::Generator.new do |g|
258
+ elements = self.entries
259
+ elements = elements.reject { |i| i.is_a?(Empty) }
260
+ (elements << item).each { |i| g.yield i }
261
+ end)
262
+ end
263
+
264
+ def to_seq
265
+ Sequence.new(Sequence::Generator.new do |g|
266
+ self.entries.map { |e| Type.check(e, Sequences::Sequence); e.entries }.flatten.each { |i| g.yield i }
267
+ end)
268
+ end
269
+
270
+ def from_pairs
271
+ Sequence.new(Sequence::Generator.new do |g|
272
+ self.entries.map { |e| Type.check(e, Pair::Pair); [e.key, e.value] }.flatten.each { |i| g.yield i }
273
+ end)
274
+ end
275
+
276
+ def to_a
277
+ execution = {
278
+ Sequences::Sequence => -> { self.entries.map { |s| s.entries } },
279
+ Pair::Pair => -> { self.entries.map { |pair| pair.to_map } }
280
+ }
281
+ if self.empty?
282
+ self.entries
283
+ else
284
+
285
+ first_item = self.peek_values.first.class
286
+ execution[first_item].nil? ? self.entries : execution[first_item].call
287
+ end
288
+ end
289
+
290
+ def all
291
+ to_a.flatten
292
+ end
293
+
294
+ def get_or_else(index, or_else)
295
+ blank?(sequence[index]) ? or_else : sequence[index]
296
+ end
297
+
298
+ def get_option(index)
299
+ blank?(sequence[index]) ? none : some(sequence[index])
300
+ end
301
+
302
+
228
303
  private
229
304
  def sequence
230
305
  Sequence.new(self)
231
306
  end
232
307
 
308
+ def blank?(item)
309
+ item.respond_to?(:empty?) ? item.empty? : !item
310
+ end
311
+
312
+ end
313
+
314
+ class Empty < Sequence
315
+
316
+ def initialize(obj=[], &block)
317
+ super(obj) { |yielder|
318
+ begin
319
+ obj.each { |x|
320
+ if block
321
+ block.call(yielder, x)
322
+ else
323
+ yielder << x
324
+ end
325
+ }
326
+ rescue StopIteration
327
+ end
328
+ }
329
+ end
330
+
233
331
  end
234
332
 
235
333
  end
@@ -246,3 +344,5 @@ end
246
344
 
247
345
 
248
346
 
347
+
348
+
data/lib/totally_lazy.rb CHANGED
@@ -1,8 +1,13 @@
1
+ require_relative 'type_check'
1
2
  require_relative 'sequence'
2
3
  require_relative 'pair'
3
4
  require_relative 'option'
5
+ require_relative 'functor'
6
+ require_relative 'predicates'
4
7
 
5
8
  include Sequences
6
9
  include Option
7
10
  include Pair
11
+ include Predicates::Numbers
12
+ include Predicates::Conversions
8
13
 
data/lib/type_check.rb ADDED
@@ -0,0 +1,11 @@
1
+ class Type
2
+
3
+ def self.check(actual, expected)
4
+ raise(UnsupportedTypeException.new, "Target must be of type: #{expected} but was: #{actual.class}") unless actual.kind_of?(expected)
5
+ end
6
+
7
+ def self.responds(value,meth)
8
+ raise(UnsupportedTypeException.new, "Target must respond to: #{meth} - but did not with: #{value.class} of: #{value}") unless value.respond_to?(meth)
9
+ end
10
+
11
+ end
data/spec/option_spec.rb CHANGED
@@ -4,7 +4,7 @@ require_relative '../lib/totally_lazy'
4
4
  describe 'Option' do
5
5
 
6
6
  it 'should support contains' do
7
- expect(option(1).contains(1)).to be(true)
7
+ expect(option(1).contains(1)).to eq(true)
8
8
  end
9
9
 
10
10
  it 'should support is alias' do
@@ -26,7 +26,8 @@ describe 'Option' do
26
26
  end
27
27
 
28
28
  it 'should not get value of none' do
29
-
29
+ pending('not get none')
30
+ pass
30
31
  end
31
32
 
32
33
  it 'should get or else' do
data/spec/pair_spec.rb CHANGED
@@ -22,7 +22,7 @@ describe 'Pair' do
22
22
  end
23
23
 
24
24
  it 'should convert to a sequence of pairs from a map' do
25
- expect(Pair.from_map({apples:'10'})).to eq(sequence(pair(:apples,'10')))
25
+ expect(Pair.from_map({apples:'10'}).to_a).to eq(sequence(pair(:apples,'10')).to_a)
26
26
  end
27
27
 
28
28
  it 'should convert a pair to a map' do
@@ -8,7 +8,7 @@ describe 'Sequence' do
8
8
  end
9
9
 
10
10
  it 'should support transpose' do
11
- expect(sequence(sequence(1, 2), sequence(3, 4), sequence(5, 6))).to include(sequence(1, 3, 5), sequence(2, 4, 6))
11
+ expect(sequence(sequence(1, 2), sequence(3, 4), sequence(5, 6)).transpose).to include(sequence(1, 3, 5), sequence(2, 4, 6))
12
12
  end
13
13
 
14
14
  it 'should eagerly return the first element of a sequence, returns NoSuchElementException if empty' do
@@ -46,5 +46,10 @@ describe 'Sequence' do
46
46
  expect { empty.shuffle.first }.to raise_error(NoSuchElementException)
47
47
  end
48
48
 
49
+ it 'should lazily join sequences together' do
50
+ expect(sequence(1,2,3).join(sequence(4,5,6))).to eq(sequence(1,2,3,4,5,6))
51
+ expect(empty.join(sequence(1,2))).to eq(sequence(1,2))
52
+ end
53
+
49
54
 
50
55
  end
data/totally_lazy.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: totally_lazy 0.0.2 ruby lib
5
+ # stub: totally_lazy 0.0.3 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "totally_lazy"
9
- s.version = "0.0.2"
9
+ s.version = "0.0.3"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Kingsley Hendrickse"]
14
- s.date = "2014-08-15"
14
+ s.date = "2014-08-16"
15
15
  s.description = "Port of java functional library totally lazy to ruby"
16
16
  s.email = "kingsleyhendrickse@me.com"
17
17
  s.extra_rdoc_files = [
@@ -26,11 +26,14 @@ Gem::Specification.new do |s|
26
26
  "README.md",
27
27
  "Rakefile",
28
28
  "VERSION",
29
+ "lib/functor.rb",
29
30
  "lib/option.rb",
30
31
  "lib/pair.rb",
32
+ "lib/predicates.rb",
31
33
  "lib/sequence.rb",
32
34
  "lib/totally_lazy.rb",
33
35
  "lib/tuple.rb",
36
+ "lib/type_check.rb",
34
37
  "spec/option_spec.rb",
35
38
  "spec/pair_spec.rb",
36
39
  "spec/sequence_spec.rb",
@@ -51,12 +54,14 @@ Gem::Specification.new do |s|
51
54
  s.add_development_dependency(%q<bundler>, ["~> 1.0"])
52
55
  s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
53
56
  s.add_development_dependency(%q<rspec_html_formatter>, ["~> 0.3.0"])
57
+ s.add_development_dependency(%q<rake>, ["~> 10.3.2"])
54
58
  else
55
59
  s.add_dependency(%q<rspec>, ["~> 3.0.0"])
56
60
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
57
61
  s.add_dependency(%q<bundler>, ["~> 1.0"])
58
62
  s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
59
63
  s.add_dependency(%q<rspec_html_formatter>, ["~> 0.3.0"])
64
+ s.add_dependency(%q<rake>, ["~> 10.3.2"])
60
65
  end
61
66
  else
62
67
  s.add_dependency(%q<rspec>, ["~> 3.0.0"])
@@ -64,6 +69,7 @@ Gem::Specification.new do |s|
64
69
  s.add_dependency(%q<bundler>, ["~> 1.0"])
65
70
  s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
66
71
  s.add_dependency(%q<rspec_html_formatter>, ["~> 0.3.0"])
72
+ s.add_dependency(%q<rake>, ["~> 10.3.2"])
67
73
  end
68
74
  end
69
75
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: totally_lazy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kingsley Hendrickse
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-15 00:00:00.000000000 Z
11
+ date: 2014-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.3.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 10.3.2
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 10.3.2
83
97
  description: Port of java functional library totally lazy to ruby
84
98
  email: kingsleyhendrickse@me.com
85
99
  executables: []
@@ -95,11 +109,14 @@ files:
95
109
  - README.md
96
110
  - Rakefile
97
111
  - VERSION
112
+ - lib/functor.rb
98
113
  - lib/option.rb
99
114
  - lib/pair.rb
115
+ - lib/predicates.rb
100
116
  - lib/sequence.rb
101
117
  - lib/totally_lazy.rb
102
118
  - lib/tuple.rb
119
+ - lib/type_check.rb
103
120
  - spec/option_spec.rb
104
121
  - spec/pair_spec.rb
105
122
  - spec/sequence_spec.rb