comprehensible 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # Comprehensible
2
+
3
+ Comprehensible is a way to build list comprehensions in Ruby.
4
+
5
+ ### Is it a hack?
6
+
7
+ Of course.
8
+
9
+ ### Ok, so what does it do?
10
+
11
+ You pass in some source enumerables with labels along with some filtering and
12
+ mapping lambdas, and you get an array back.
13
+
14
+ ### How is the result built?
15
+
16
+ The filters are applied to their matching source in order, and the mappers are
17
+ composed based on their arguments and applied to the output of the filters.
18
+
19
+ If more than one enumerable is present prior to return, the results for each are
20
+ zipped together.
21
+
22
+ ### Can I see a few examples?
23
+
24
+ Sure.
25
+
26
+ ```ruby
27
+ # Square the even xs
28
+ Array.comprehension({x: 1..10}, [->(x) { x.even? }], [->(x) { x ** x }])
29
+ => [4, 256, 46656, 16777216, 10000000000]
30
+
31
+ # Zip odd x and even y pairs
32
+ Array.comprehension({x: 1..10, y: 1..10}, [->(x) { x.odd? }, ->(y) { y.even? }], [])
33
+ => [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
34
+
35
+ # Solution to Euler 1 (http://projecteuler.net/problem=1)
36
+ Array.comprehension({x: 1...1000}, [->(x) { x % 3 == 0 || x % 5 == 0 }], []).reduce(:+)
37
+ => 233168
38
+
39
+ # log2 growth
40
+ Hash[Array.comprehension({x: 1..1000, y: 1..1000}, [], [->(y) { Math.log2(y) }])]
41
+ => {1=>0.0,
42
+ 2=>1.0,
43
+ 3=>1.584962500721156,
44
+ [...]
45
+ 998=>9.96289600533726,
46
+ 999=>9.964340867792417,
47
+ 1000=>9.965784284662087}
48
+ ```
49
+
50
+ ### How can I play with this?
51
+
52
+ `gem install comprehensible`
53
+
54
+ Then you should be able to use it with
55
+
56
+ `require comprehensible`
57
+
58
+ ### What are the limitations?
59
+
60
+ There are some (lots):
61
+
62
+ * Function argument names must match a key in the enums hash.
63
+ * Lambdas can only have a single argument, otherwise execution ordering is a headache.
64
+ * Filters are run before mappers, you can't filter using mappers either
65
+
66
+ :(
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'rake'
2
+ require 'rspec/core/rake_task'
3
+ require 'bundler/gem_tasks'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -0,0 +1,42 @@
1
+ module Comprehensible
2
+ def comprehension(labeled_enums, filters=[], mappers=[])
3
+ filters = construct_filters(filters)
4
+ mappers = construct_mappers(mappers)
5
+
6
+ filtered = labeled_enums.reduce({}) do |accum, (key, values)|
7
+ values.each do |val|
8
+ inc = filters[key].map { |filter| filter[val] }.all?
9
+
10
+ (accum[key] ||= []) << val if inc
11
+ end
12
+ accum
13
+ end
14
+
15
+ mapped = filtered.reduce({}) do |accum, (key, values)|
16
+ mappers[key] ? accum[key] = values.map { |value| mappers[key][value] } : accum[key] = values
17
+ accum
18
+ end
19
+
20
+ self.new(mapped.keys.count >= 2 ? mapped.values.self_zip : mapped.values.flatten)
21
+ end
22
+
23
+ private
24
+
25
+ def construct_mappers(mappers)
26
+ grouped_mappers = mappers.group_by(&:extract_required_parameters)
27
+
28
+ composed_mappers = grouped_mappers.reduce({}) do |accum, (key, values)|
29
+ accum[key] = values.reduce(:*)
30
+ accum
31
+ end
32
+
33
+ composed_mappers
34
+ end
35
+
36
+ def construct_filters(filters)
37
+ grouped_filters = filters.group_by(&:extract_required_parameters)
38
+ grouped_filters.default = []
39
+
40
+ grouped_filters
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ class Array
2
+ extend Comprehensible
3
+
4
+ def self_zip
5
+ head, *tail = self.sort_by(&:length)
6
+ head.zip(*tail)
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ class Proc
2
+ def self.compose(f, g)
3
+ lambda { |*args| f[g[*args]] }
4
+ end
5
+
6
+ def *(g)
7
+ Proc.compose(self, g)
8
+ end
9
+
10
+ def extract_required_parameters
11
+ Hash[self.parameters][:req]
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module Comprehensible
2
+ MAJOR = 0
3
+ MINOR = 1
4
+ MICRO = 0
5
+
6
+ VERSION = [MAJOR, MINOR, MICRO].join('.')
7
+ end
@@ -0,0 +1,18 @@
1
+ module Comprehensible
2
+ class << self
3
+ def root
4
+ @@root ||= File.expand_path(File.dirname(__FILE__))
5
+ end
6
+
7
+ def require_all path
8
+ Dir[File.join(root, path, '*.rb')].each { |f| require f }
9
+ end
10
+
11
+ def load_all path
12
+ Dir[File.join(root, path, '*.rb')].each { |f| load f }
13
+ end
14
+ end
15
+ end
16
+
17
+ Comprehensible::require_all('comprehensible/')
18
+ Comprehensible::load_all('comprehensible/core_extensions')
@@ -0,0 +1,23 @@
1
+ require File.expand_path('../../../lib/comprehensible', __FILE__)
2
+
3
+ describe :comprehension do
4
+ it "should return its input as a list without modifiers" do
5
+ Array.comprehension({x: 1..10}).should eq (1..10).to_a
6
+ end
7
+
8
+ it "should zip multiple enums correctly without modifiers" do
9
+ Array.comprehension({x: 1..10, y: 1..99}).should eq (1..10).to_a.zip((1..99).to_a)
10
+ end
11
+
12
+ it "should filter non-matching values using filters" do
13
+ Array.comprehension({x: 1..10}, [->(x) { x > 5 }, ->(x) { x < 7}]).should eq [6]
14
+ end
15
+
16
+ it "should modify applicable values with mapping operations" do
17
+ Array.comprehension({x: 1..3}, [], [->(x) { x ** x }, ->(x) { x ** x }]).should eq [1, 256, 443426488243037769948249630619149892803]
18
+ end
19
+
20
+ it "should apply mapping operations to filtered values" do
21
+ Array.comprehension({x: 1..10}, [->(x) { x.odd? }], [->(x) { x ** x }]).should eq [1, 27, 3125, 823543, 387420489]
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: comprehensible
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matt Smith
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-09-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Map and filter enumerables into lists of your design
47
+ email: matt@thunk.io
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - lib/comprehensible/comprehension.rb
53
+ - lib/comprehensible/core_extensions/array.rb
54
+ - lib/comprehensible/core_extensions/proc.rb
55
+ - lib/comprehensible/version.rb
56
+ - lib/comprehensible.rb
57
+ - spec/comprehensible/comprehension_spec.rb
58
+ - README.md
59
+ - Rakefile
60
+ homepage: http://github.com/qbyt/comprehensible
61
+ licenses:
62
+ - MIT
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project: ''
81
+ rubygems_version: 1.8.23
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: List comprehensions... ish
85
+ test_files: []
86
+ has_rdoc: false