comprehensible 0.1.0
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.
- data/README.md +66 -0
- data/Rakefile +7 -0
- data/lib/comprehensible/comprehension.rb +42 -0
- data/lib/comprehensible/core_extensions/array.rb +8 -0
- data/lib/comprehensible/core_extensions/proc.rb +13 -0
- data/lib/comprehensible/version.rb +7 -0
- data/lib/comprehensible.rb +18 -0
- data/spec/comprehensible/comprehension_spec.rb +23 -0
- metadata +86 -0
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,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,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
|