functions 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format progress
3
+ # --default_path spec
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in functions.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'test-unit'
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Koen Handekyn
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Functional
2
+
3
+ This library facilitates writing code in a more functional style inside Ruby.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'functional'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install functional
18
+
19
+ ## Usage
20
+
21
+ The library provides two different styles.
22
+ One style builds on Lambda's and another style uses meta-programming.
23
+ The lambda style is a bit more pure but is currently just a bit slower in performance.
24
+
25
+ ### Lambda Style
26
+
27
+ ### Meta Style
28
+
29
+ ## Contributing
30
+
31
+ 1. Fork it
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test" << "."
6
+ t.test_files = FileList['test/test*.rb']
7
+ t.verbose = false
8
+ end
@@ -0,0 +1,45 @@
1
+ require 'functions'
2
+
3
+ module PreludeLambdaUsage
4
+
5
+ include Functions::Prelude
6
+
7
+ Power = ->(p,x) { x**p }.curry
8
+ Square = Power.(2)
9
+ Squares = Map.(Square)
10
+ Sum_Of_Squares1 = Compose.(Sum).(Squares)
11
+ Sum_Of_Squares2 = Sum < Squares
12
+ Sum_Of_Squares = Sum_Of_Squares2
13
+
14
+ Inc_With = ->(f, n, m) { n + f.(m) }.curry
15
+ Sum_Of1 = ->(f,arr) { Foldl.( Inc_With.(f), 0, arr) }.curry
16
+ Sum_Of2 = ->(f) { ->(arr) { Foldl.( Inc_With.(f), 0, arr) } }
17
+ Sum_Of = Sum_Of1
18
+
19
+ # Average variations
20
+ Average1 = After.( Parallel.(Sum).(Length) ).( Divide )
21
+ Average2 = After.( Par.([Sum,Length]) ).( Divide )
22
+ Average3 = After.( [Sum,Length] ).( Divide )
23
+
24
+ # Gcd variations
25
+ Gcd1 = ->(a, b) {
26
+ (1..[a,b].min).select { |c| a % c == 0 && b % c == 0 }.max
27
+ }
28
+ Gcd2 = ->(a,b) { (Divisors.(a) & Divisors.(b)).max }
29
+
30
+ # GcdA variations
31
+ GcdA1 = Max < Intersect < Map.(Divisors) # litteral translation of the definition
32
+ GcdA2 = ->(arr) { arr.inject { |a, r| Gcd.(a, r) } } # the gcd of an array as an iteration of the gcd over the elements
33
+ GcdA3 = ReduceLeft.(Gcd) # the same but written without arguments
34
+
35
+ Greatest = Max
36
+ Common = Intersect
37
+ Dividers = Map.(Divisors)
38
+ GcdA4 = Greatest < Common < Dividers
39
+
40
+
41
+ # LcmA variations
42
+ LcmA1 = ->(arr) { arr.inject { |a, r| Lcm.(a, r) } } # the lcm of an array as an iteration of the lcm over the elements
43
+ LcmA2 = ReduceLeft.(Lcm) # the same but without arguments
44
+
45
+ end
@@ -0,0 +1,72 @@
1
+ require 'functions'
2
+
3
+ module PreludeMetaUsage
4
+
5
+ include Functions::PreludeMeta
6
+
7
+ def power(x, p)
8
+ x ** p
9
+ end
10
+
11
+ def square(x)
12
+ power(x, 2)
13
+ end
14
+
15
+ def_map :squares, :square
16
+
17
+ def squares_bis(a)
18
+ map(a, :square)
19
+ end
20
+
21
+ def_map :squares_tris, ->(x) { x**2 }
22
+
23
+ define :squares_quater, as: { map: ->(x) { x**2 } }
24
+
25
+ define :squares_quinquies, as: { map: :square }
26
+
27
+ def_filter :evens, :even?
28
+
29
+ # same as above
30
+ # filter :evens, :even?
31
+
32
+ def_filter :odds, ->(x) { ! x.even? }
33
+
34
+ define :evens_bis, as: { filter: :even? }
35
+
36
+ def_compose :sum_of_squares, :sum, :squares
37
+
38
+ def sum_of_squares_bis(a)
39
+ compose(:sum, :squares, a)
40
+ end
41
+
42
+ define :sum_of_squares_tris, as: { compose: [:sum, :squares] }
43
+
44
+ def average_bis(a)
45
+ after([:sum, :length], :divide, a)
46
+ end
47
+
48
+ define :average_tris, as: { :after => [ :sum_length, :divide ] }
49
+
50
+ def_after :average_quater, :sum_length, :divide
51
+
52
+ def inc_with(f, n, m)
53
+ m + f(f).(n)
54
+ end
55
+
56
+ # can this be improved on ?
57
+ # a definition as composition ?
58
+ def sum_of(f, a)
59
+ a.inject(0) { |r, x| r + f(f).(x) }
60
+ end
61
+
62
+ def add(a,b)
63
+ a+b
64
+ end
65
+
66
+ def_foldl :sum_bis, :add, 0
67
+
68
+ define :sum_tris, as: { foldl: [:add, 0]}
69
+
70
+ define foldl: [:add, 0], as: :sum_quater
71
+
72
+ end
@@ -0,0 +1,48 @@
1
+ require_relative '../prelude_lambda'
2
+
3
+ include PreludeLambdaUsage
4
+
5
+ describe PreludeLambdaUsage, "basic lambda prelude usage" do
6
+
7
+ it "sums" do
8
+ Sum_Of_Squares.([2, 3, 4]).should == 2*2+3*3+4*4
9
+ Sum_Of.(Square).([2, 3, 1]).should == 2*2+3*3+1*1
10
+ end
11
+
12
+ it "averages" do
13
+ Average.([2, 3, 8]).should == 4
14
+ end
15
+
16
+ it "flattens" do
17
+ Flatten.([[1, 2, 3], [2, 3]]).should == [1, 2, 3, 2, 3]
18
+ end
19
+
20
+ it "folds" do
21
+ Foldl.(->(n, a) { n/a }, 1.0, [1.0, 2.0, 3.0]).should == 1.0/1.0/2.0/3.0
22
+ Foldr.(->(n, a) { n/a }, 1.0, [1.0, 2.0, 3.0]).should == 1.0/3.0/2.0/1.0
23
+ end
24
+
25
+ it "knows about GCD alternatives" do
26
+ # test variant implementations
27
+ Gcd1.(4,8).should == 4
28
+ Gcd2.(4,8).should == 4
29
+ GcdA1.([4,8,2]).should eq(2)
30
+ GcdA2.([4,8,2]).should eq(2)
31
+ GcdA3.([4,8,2]).should eq(2)
32
+ GcdA4.([4,8,2]).should eq(2)
33
+ end
34
+
35
+ it "knows about LCM variations" do
36
+ # test variant implementations
37
+ LcmA1.([12,9,2]).should eq(36)
38
+ LcmA2.([12,9,2]).should eq(36)
39
+ end
40
+
41
+
42
+ #def bench_sum_of_squares
43
+ # assert_performance_linear 0.9 do |n| # n is a range value
44
+ # Sum_Of_Squares.((1..n).to_a)
45
+ # end
46
+ #end
47
+
48
+ end
@@ -0,0 +1,59 @@
1
+ require_relative '../prelude_meta'
2
+
3
+ include PreludeMetaUsage
4
+
5
+ describe PreludeMetaUsage, "basic meta prelude usage" do
6
+
7
+ it "squares" do
8
+ squares([1, 2, 3]).should == [1, 4, 9]
9
+ squares_bis([1, 2, 3]).should == [1, 4, 9]
10
+ squares_tris([1, 2, 3]).should == [1, 4, 9]
11
+ squares_quater([1, 2, 3]).should == [1, 4, 9]
12
+ squares_quinquies([1, 2, 3]).should == [1, 4, 9]
13
+ end
14
+
15
+ it "evens and odds" do
16
+ evens([1, 2, 3, 4, 5]).should == [2, 4]
17
+ evens_bis([1, 2, 3, 4, 5]).should == [2, 4]
18
+ odds([1, 2, 3, 4, 5]).should == [1, 3, 5]
19
+ end
20
+
21
+ it "takes sums of squares and more" do
22
+ sum_of_squares([2, 3, 4]).should == 2*2+3*3+4*4
23
+ sum_of_squares_bis([2, 3, 4]).should == 2*2+3*3+4*4
24
+ sum_of_squares_tris([2, 3, 4]).should == 2*2+3*3+4*4
25
+ sum_of(:square, [2, 3, 1]).should == 2*2+3*3+1*1
26
+ sum_of(->(x) { x*x }, [2, 3, 1]).should == 2*2+3*3+1*1
27
+ sum([1, 2, 3]).should == 1+2+3
28
+ sum_bis([1, 2, 3]).should == 1+2+3
29
+ sum_tris([1, 2, 3]).should == 1+2+3
30
+ sum_quater([1, 2, 3]).should == 1+2+3
31
+ end
32
+
33
+ it "takes averages" do
34
+ average([2, 3, 8]).should == 4
35
+ average_bis([2, 3, 8]).should == 4
36
+ average_tris([2, 3, 8]).should == 4
37
+ average_quater([2, 3, 8]).should == 4
38
+ end
39
+
40
+ it "folds" do
41
+ foldl(->(n, a) { n/a }, 1.0, [1.0, 2.0, 3.0]).should == 1.0/1.0/2.0/3.0
42
+ foldr(->(n, a) { n/a }, 1.0, [1.0, 2.0, 3.0]).should == 1.0/3.0/2.0/1.0
43
+ end
44
+
45
+ it "mins and maxes" do
46
+ min([1, 2, 3]).should == 1
47
+ max([1, 2, 3]).should == 3
48
+ end
49
+
50
+ it "makes ranges" do
51
+ from_to(2, 3).should == (2..3)
52
+ from_one_to(3).should == (1..3)
53
+ end
54
+
55
+ it "intersects arrays" do
56
+ intersect([[1, 2, 3], [2, 3, 4], [2, 3, 8]]).should == [2, 3]
57
+ end
58
+
59
+ end
data/functions.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'functions/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "functions"
8
+ gem.version = Functions::VERSION
9
+ gem.authors = ["Koen Handekyn"]
10
+ gem.email = ["koen.handekyn@up-nxt.com"]
11
+ gem.description = %q{functions programming in ruby}
12
+ gem.summary = %q{functions programming in ruby}
13
+ gem.homepage = "http://github.com"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ end
data/lib/functions.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "functions/version"
2
+ require 'functions/shared/ext'
3
+ require 'functions/prelude_lambda'
4
+ require 'functions/prelude_meta'
@@ -0,0 +1,5 @@
1
+ require_relative 'prelude_lambda/ext'
2
+ require_relative 'prelude_lambda/basic'
3
+ require_relative 'prelude_lambda/math'
4
+ require_relative 'prelude_lambda/sorting'
5
+
@@ -0,0 +1,83 @@
1
+ module Functions
2
+
3
+ module Prelude
4
+
5
+ Id = ->(x) { x }
6
+
7
+ Const = ->(c, x) { c }
8
+
9
+ Split_In = ->(n, xs) { xs.each_slice((xs.length+1)/n).to_a }
10
+
11
+ Split_In_Half = Split_In.curry.(2)
12
+
13
+ Merge_By = ->(f, xs, ys) do
14
+
15
+ return xs if ys.empty?
16
+ return ys if xs.empty?
17
+
18
+ x, *xt = xs
19
+ y, *yt = ys
20
+
21
+ return Merge_By.(f, xt, ys) >> x if f.nil? ? x <= y : f.(x) <= f.(y)
22
+ return Merge_By.(f, xs, yt) >> y
23
+
24
+ end
25
+
26
+ Merge = Merge_By.partial(nil)
27
+
28
+ Compose = ->(f, g, x) { f.(g.(x)) }.curry
29
+
30
+ # Manually curried version
31
+ # ComposeCurried = ->(f) { ->(g) { ->(x) { f.(g.(x)) } } }
32
+
33
+ After = ->(f, g, x) {
34
+ f = Par.(f) if Array === f;
35
+ g = Par.(g) if Array === g;
36
+ g.(f.(x))
37
+ }.curry
38
+
39
+ # Manually Curried Version
40
+ # AfterCurried = ->(f) { ->(g) { ->(x) {
41
+ # f = Par.(f) if Array === f;
42
+ # g = Par.(g) if Array === g;
43
+ # g.(f.(x))
44
+ # } } }
45
+
46
+ Send = ->(m, o) { o.send(m) }.curry
47
+
48
+ Flatten = Send.(:flatten)
49
+ # Flatten = ->(a) { a.flatten }
50
+
51
+ Reverse = Send.(:reverse)
52
+ # Reverse = ->(a) { a.reverse }
53
+
54
+ Length = Send.(:length)
55
+ # Length = ->(x) { x.length }
56
+
57
+ Foldl = ->(f, i, a) { a.inject(i) { |r, x| f.(r, x) } }.curry
58
+
59
+ ReduceLeft = ->(f, a) { a.inject { |r, x| f.(r, x) } }.curry
60
+
61
+ Foldr = ->(f, i, a) { Foldl.(f, i, a.reverse) }
62
+
63
+ ReduceRight = ->(f, a) { ReduceLeft.(f, a.reverse) }.curry
64
+
65
+ Zip = ->(a, b) { a.zip(b) }.curry
66
+
67
+ Map = ->(f, a) { a.map { |x| f.(x) } }.curry
68
+
69
+ Filter = ->(xs, f) { xs.select { |x| f.(x) } }.curry
70
+
71
+ Parallel = ->(f, g, x) { [f.(x), g.(x)] }.curry
72
+
73
+ Par = ->(fs, x) { fs.map { |f| f.(x) } }.curry
74
+
75
+ Intersect = ->(as) { as.inject(:&) }
76
+
77
+ FromTo = ->(from) { ->(to) { Range.new(from, to) } }
78
+
79
+ FromOneTo = FromTo.(1)
80
+
81
+ end
82
+
83
+ end
@@ -0,0 +1,32 @@
1
+ # reference http://iain.nl/going-crazy-with-to_proc
2
+
3
+ class Array
4
+ # converts the array into a Proc where
5
+ # the first element is the method name, other elements are the values
6
+ def to_proc
7
+ head, *tail = *self
8
+ Proc.new { |obj, *other| obj.__send__(head, *(other + tail)) }
9
+ end
10
+ end
11
+
12
+ class Proc
13
+ # Composition of Procs (functions): (f+g)(x) = f(g(x))
14
+ def +(g)
15
+ # Prelude::Compose.(self,g)
16
+ ->(x) { self.(g.(x)) }
17
+ end
18
+ # Composition of Procs (functions): (f<g)(x) = f(g(x))
19
+ def <(g)
20
+ # Prelude::Compose.(self,g)
21
+ ->(x) { self.(g.(x)) }
22
+ end
23
+ # Composition of Procs (functions): (f>g)(x) = g(f(x))
24
+ def >(g)
25
+ # Prelude::After.(self,g)
26
+ ->(x) { g.(self.(x)) }
27
+ end
28
+ # Partial evaluation of a Proc (function)
29
+ def partial(a)
30
+ self.curry.(a)
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ module Functions
2
+
3
+ module Prelude
4
+
5
+ Max = Send.(:max)
6
+
7
+ Min = Send.(:min)
8
+
9
+ Sum = ->(arr) { arr.inject(0, :+) }
10
+
11
+ Multiply = ->(arr) { arr.inject(1, :*) }
12
+
13
+ Divide = ->(arr) { arr.inject(:/) }
14
+
15
+ Average = Parallel.(Sum,Length) > Divide
16
+
17
+ IsDivisor = ->(a, c) { a % c == 0 }
18
+
19
+ Divisors = ->(a) { Filter.(FromOneTo.(a), IsDivisor.partial(a)) }
20
+
21
+ Gcd = ->(a, b) { b == 0 ? a : Gcd.(b, a % b) } # euclid algorithm
22
+
23
+ GcdA = ReduceLeft.(Gcd)
24
+
25
+ Lcm = ->(a, b) { a*b / Gcd.(a, b) } # mathematical law
26
+
27
+ LcmA = ReduceLeft.(Lcm)
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,27 @@
1
+ module Functions
2
+
3
+ module Prelude
4
+
5
+ Merge_Sort_By = ->(f, xs) do
6
+
7
+ return xs if xs.length <= 1 # stopcondition
8
+
9
+ left, right = Split_In_Half.(xs)
10
+ Merge_By.(f, Merge_Sort_By.(f, left), Merge_Sort_By.(f, right))
11
+
12
+ end
13
+
14
+ Merge_Sort = Merge_Sort_By.partial(nil)
15
+
16
+ Quick_Sort_By = ->(f, list) do
17
+ return [] if list.size == 0
18
+ pivot, *xs = *list
19
+ smaller_than = f.nil? ? ->(y) { y < pivot } : ->(y) { f.(y) < f.(pivot) }
20
+ less, more = xs.partition &smaller_than
21
+ Quick_Sort_By.(f, less) + [pivot] + Quick_Sort_By.(f, more)
22
+ end
23
+
24
+ Quick_Sort = Quick_Sort_By.partial(nil)
25
+
26
+ end
27
+ end
@@ -0,0 +1,306 @@
1
+ module Functions
2
+
3
+ module PreludeMeta
4
+
5
+ def self.included(klass)
6
+ klass.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def create_proc_method(c, f, i=:f)
12
+ if Proc === f
13
+ define_method("#{c}_#{i}_proc", f)
14
+ return "#{c}_#{i}_proc"
15
+ else
16
+ return f
17
+ end
18
+ end
19
+
20
+ def define_v1(c, definition)
21
+ key, value = definition[:as].first
22
+ self.send(key, c, *value)
23
+ end
24
+
25
+ def define_v2(definition, name)
26
+ key, value = definition
27
+ self.send(key, name, *value)
28
+ end
29
+
30
+ def define(*params)
31
+ if Symbol === params[0]
32
+ define_v1(*params)
33
+ else
34
+ name = params[0].delete(:as)
35
+ define_v2(*params[0], name)
36
+ end
37
+ end
38
+
39
+ def compose(c, f, g)
40
+ m = create_proc_method(c, f, :f)
41
+ n = create_proc_method(c, g, :g)
42
+ code = %Q{
43
+ def #{c}(x)
44
+ #{m}(#{n}(x))
45
+ end
46
+ }
47
+ module_eval(code)
48
+ end
49
+
50
+ alias :def_compose :compose
51
+
52
+ def after(c, f, g)
53
+ compose(c, g, f)
54
+ end
55
+
56
+ alias :def_after :after
57
+
58
+ def parallel(c, f, g)
59
+ m = create_proc_method(c, f, :f)
60
+ n = create_proc_method(c, g, :g)
61
+ code = %Q{
62
+ def #{c}(x)
63
+ [#{m}(x),#{n}(x)]
64
+ end
65
+ }
66
+ module_eval(code)
67
+ end
68
+
69
+ alias :def_parallel :parallel
70
+
71
+ def foldl(c, f, i)
72
+ m = create_proc_method(c, f)
73
+ code = %Q{
74
+ def #{c}(arr)
75
+ arr.inject(#{i}) { |r, x| #{m}(r,x) }
76
+ end
77
+ }
78
+ module_eval(code)
79
+ end
80
+
81
+ alias :def_foldl :foldl
82
+
83
+ def reduce_left(c, f)
84
+ m = create_proc_method(c, f)
85
+ code = %Q{
86
+ def #{c}(arr)
87
+ arr.inject { |r, x| #{m}(r,x) }
88
+ end
89
+ }
90
+ module_eval(code)
91
+ end
92
+
93
+ alias :def_reduce_left :reduce_left
94
+
95
+ def foldr(c, f, i)
96
+ m = create_proc_method(c, f)
97
+ code = %Q{
98
+ def #{c}(arr)
99
+ arr.reverse.inject(#{i}) { |r, x| #{m}(r,x) }
100
+ end
101
+ }
102
+ module_eval(code)
103
+ end
104
+
105
+ alias :def_foldr :foldr
106
+
107
+ def reduce_right(c, f)
108
+ m = create_proc_method(c, f)
109
+ code = %Q{
110
+ def #{c}(arr)
111
+ arr.reverse.inject { |r, x| #{m}(r,x) }
112
+ end
113
+ }
114
+ module_eval(code)
115
+ end
116
+
117
+ alias :def_reduce_right :reduce_right
118
+
119
+ def map(c, f)
120
+ m = create_proc_method(c, f)
121
+ code = %Q{
122
+ def #{c}(arr)
123
+ arr.map { |x| #{m}(x) }
124
+ end
125
+ }
126
+ module_eval(code)
127
+ end
128
+
129
+ alias :def_map :map
130
+
131
+ def filter(c, f)
132
+ m = create_proc_method(c, f)
133
+ code = %Q{
134
+ def #{c}(arr)
135
+ arr.select { |x| #{m}(x) }
136
+ end
137
+ }
138
+ module_eval(code)
139
+ end
140
+
141
+ alias :def_filter :filter
142
+
143
+ def method(m)
144
+ code = %Q{
145
+ def #{m}(o)
146
+ o.#{m}
147
+ end
148
+ }
149
+ module_eval(code)
150
+ end
151
+
152
+ alias :def_method :method
153
+
154
+ end
155
+
156
+ def m(method)
157
+ lambda(&method(method))
158
+ end
159
+
160
+ def f(f)
161
+ Symbol === f ? f = m(f) : f
162
+ end
163
+
164
+ def id(x)
165
+ x
166
+ end
167
+
168
+ def const(c, x)
169
+ c
170
+ end
171
+
172
+ def split_in(xs, n)
173
+ xs.each_slice((xs.length+1)/n).to_a
174
+ end
175
+
176
+ def split_in_half(xs)
177
+ split_in(xs, 2)
178
+ end
179
+
180
+ def merge(xs, ys, &by)
181
+
182
+ return xs if ys.empty?
183
+ return ys if xs.empty?
184
+
185
+ x, *xt = xs
186
+ y, *yt = ys
187
+
188
+ return merge(xt, ys, &by) >> x if block_given? ? by.(x) <= by.(y) : x <= y
189
+ return merge(xs, yt, &by) >> y
190
+
191
+ end
192
+
193
+ def merge_sort(xs, &by)
194
+ return xs if xs.length <= 1 # stopcondition
195
+ left, right = split_in_half(xs)
196
+ merge(merge_sort(left, &by), merge_sort(right, &by), &by)
197
+ end
198
+
199
+ def quick_sort(list)
200
+ return [] if list.size == 0
201
+ pivot, *xs = *list
202
+ less, more = xs.partition { |y| y < pivot }
203
+ quick_sort(less) + [pivot] + quick_sort(more)
204
+ end
205
+
206
+ def compose(f, g, x)
207
+ f = f(f)
208
+ g = f(g)
209
+ f.(g.(x))
210
+ end
211
+
212
+ def after(f, g, x)
213
+ f = f(f)
214
+ g = f(g)
215
+ return g.(par(*f, x)) if Array === f && Proc === g
216
+ return par(*g, f.(x)) if Proc === f && Array === g
217
+ return par(*g, par(*f, x)) if Array === f && Array === g
218
+ return g.(f.(x)) # else
219
+ end
220
+
221
+ extend ClassMethods
222
+
223
+ def_method :flatten
224
+
225
+ #def flatten(a)
226
+ # a.flatten
227
+ #end
228
+
229
+ def_method :length
230
+
231
+ #def length(a)
232
+ # a.length
233
+ #end
234
+
235
+ def_method :reverse
236
+
237
+ #def reverse(a)
238
+ # a.reverse
239
+ #end
240
+
241
+ def foldl(f, i, a)
242
+ a.inject(i) { |r, x| f(f).(r, x) }
243
+ end
244
+
245
+ def foldr(f, i, a)
246
+ foldl(f(f), i, a.reverse)
247
+ end
248
+
249
+ def zip(a, b)
250
+ a.zip(b)
251
+ end
252
+
253
+ def map(a, f)
254
+ a.map { |x| f(f).(x) }
255
+ end
256
+
257
+ def filter(a, f)
258
+ a.select { |x| f(f).(x) }
259
+ end
260
+
261
+ def sum(a)
262
+ a.inject(0, :+)
263
+ end
264
+
265
+ def parallel(f, g, x)
266
+ [f(f).(x), f(g).(x)]
267
+ end
268
+
269
+ def par(*fs, x)
270
+ fs.map { |f| f(f).(x) }
271
+ end
272
+
273
+ def_method :even?
274
+
275
+ def_method :odd?
276
+
277
+ def divide(arr)
278
+ arr.inject { |a,b| a/b }
279
+ end
280
+
281
+ define :sum_length, as: { parallel: [:sum, :length] }
282
+
283
+ define :average, as: { :after => [ :sum_length, :divide ] }
284
+
285
+ # TODO write with operator :& ?
286
+ # def_reduce_left :intersect, ->(a,b) { a&b } # short
287
+ # reduce_left :intersect, ->(a,b) { a&b } # longer
288
+ define :intersect, as: {reduce_left: ->(a, b) { a&b }} # longest
289
+
290
+ def_method :min
291
+
292
+ def_method :max
293
+
294
+ def from_to(from, to)
295
+ Range.new(from, to)
296
+ end
297
+
298
+ def from_one_to(to)
299
+ Range.new(1, to)
300
+ end
301
+
302
+ end
303
+
304
+ end
305
+
306
+
@@ -0,0 +1,6 @@
1
+ class Array
2
+ # prepend element a to the array (self)
3
+ def >> a
4
+ self.unshift a
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module Functions
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,49 @@
1
+ require_relative '../examples/prelude_usage'
2
+ require_relative '../examples/prelude_meta_usage'
3
+
4
+ include PreludeUsage
5
+ include PreludeUsageWithDefs
6
+
7
+ require 'benchmark'
8
+
9
+ [10, 100, 1000, 10000].each do |n|
10
+ Benchmark.bm(40) do |b|
11
+ b.report("Sum_Of_Squares(n=#{n}): ") { (100000/n).times { Sum_Of_Squares.((1..n).to_a) } }
12
+ b.report("sum_of_squares(n=#{n}): ") { (100000/n).times { sum_of_squares ((1..n).to_a) } }
13
+ end
14
+ end
15
+
16
+ [10, 100, 1000, 10000].each do |n|
17
+ Benchmark.bm(40) do |b|
18
+ b.report("Average(n=#{n}): ") { (100000/n).times { Average.((1..n).to_a) } }
19
+ b.report("average(n=#{n}): ") { (100000/n).times { average ((1..n).to_a) } }
20
+ end
21
+ end
22
+
23
+ [10, 100, 1000, 10000].each do |n|
24
+ Benchmark.bm(40) do |b|
25
+ b.report("Sum.(n=#{n}): ") { (100000/n).times { Sum.((1..n).to_a) } }
26
+ b.report("sum(n=#{n}): ") { (100000/n).times { sum((1..n).to_a) } }
27
+ b.report("sum_bis(n=#{n}): ") { (100000/n).times { sum_bis((1..n).to_a) } }
28
+ b.report("sum_tris(n=#{n}): ") { (100000/n).times { sum_tris((1..n).to_a) } }
29
+ b.report("sum_quater(n=#{n}): ") { (100000/n).times { sum_quater((1..n).to_a) } }
30
+ end
31
+ end
32
+
33
+ [10, 100, 1000].each do |n|
34
+ random_array = (0..n).to_a.shuffle
35
+ Benchmark.bm(40) do |b|
36
+ b.report("lambda/merge_sort(n=#{n}): ") { (100000/n).times { Merge_Sort.(random_array) } }
37
+ b.report("def/merge_sort(n=#{n}): ") { (100000/n).times { merge_sort(random_array) } }
38
+ end
39
+ end
40
+
41
+ [10, 100, 1000].each do |n|
42
+ random_array = (0..n).to_a.shuffle
43
+ Benchmark.bm(40) do |b|
44
+ b.report("lambda/quick_sort(n=#{n}): ") { (100000/n).times { Quick_Sort.(random_array) } }
45
+ # b.report("lambda/quick_sort/nil(n=#{n}): ") { (100000/n).times { Quick_Sort_By.(nil, random_array) } } # the same by definition
46
+ b.report("lambda/quick_sort/identity(n=#{n}): ") { (100000/n).times { Quick_Sort_By.(Id, random_array) } }
47
+ b.report("def/quick_sort(n=#{n}): ") { (100000/n).times { quick_sort(random_array) } }
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ require 'functions'
2
+
3
+ include Functions::Prelude
4
+
5
+ describe Functions::Prelude, "math" do
6
+
7
+ it "divides" do
8
+ Divide.([9,2]).should eq(9/2)
9
+ Divide.([8,2,2]).should eq(8/2/2)
10
+ end
11
+
12
+ it "knows about divisors" do
13
+ IsDivisor.(8,2).should eq(true)
14
+ IsDivisor.(8,3).should eq(false)
15
+ Divisors.(12).sort.should eq([1,2,3,4,6,12])
16
+ end
17
+
18
+ it "knows about range building" do
19
+ FromOneTo.(3).should eq(1..3)
20
+ FromOneTo.(3).to_a.should eq([1,2,3])
21
+ FromTo.(2).(8).to_a.should eq([2,3,4,5,6,7,8])
22
+ end
23
+
24
+ it "knows about gcd" do
25
+ Gcd.(12,9).should eq(3)
26
+ Gcd.(4,8).should eq(4)
27
+ GcdA.([12,9,6]).should eq(3)
28
+ GcdA.([4,8,2]).should eq(2)
29
+ end
30
+
31
+ it "knows about lcm" do
32
+ Lcm.(12,9).should eq(36)
33
+ Lcm.(6,8).should eq(24)
34
+ LcmA.([12,9]).should eq(36)
35
+ LcmA.([12,9,2]).should eq(36)
36
+ end
37
+
38
+ end
39
+
40
+
41
+
@@ -0,0 +1,46 @@
1
+ require 'functions'
2
+
3
+ include Functions::Prelude
4
+
5
+ describe Functions::Prelude, "basic" do
6
+
7
+ Power = ->(p, x) { x**p }.curry
8
+ Square = Power.(2)
9
+ Squares = Map.(Square)
10
+
11
+ it "composes" do
12
+ sum_of_squares = Compose.(Sum).(Squares)
13
+ sum_of_squares.([2, 3, 4]).should eq(4+9+16)
14
+ end
15
+
16
+ it "has a compose operator" do
17
+ sum_of_squares = Sum < Squares
18
+ sum_of_squares.([2, 3, 4]).should eq(4+9+16)
19
+ end
20
+
21
+ it "folds" do
22
+ inc_with = ->(f, n, m) { n + f.(m) }.curry
23
+ sum_of = ->(f, arr) { Foldl.(inc_with.(f), 0, arr) }.curry
24
+ sum_of.(Square).([1,2,3]).should eq(1+4+9)
25
+ end
26
+
27
+ it "composes using after with implicit parallel" do
28
+ average = After.([Sum, Length]).(Divide)
29
+ average.([2, 3, 8]).should eq((2+3+8)/3)
30
+ end
31
+
32
+ it "mixes parallel with after operator" do
33
+ average = Parallel.(Sum, Length) > Divide
34
+ average.([2, 3, 8]).should eq((2+3+8)/3)
35
+ end
36
+
37
+ it "mixes par with after operator" do
38
+ average = Par.([Sum, Length]) > Divide
39
+ average.([2, 3, 8]).should eq((2+3+8)/3)
40
+ end
41
+
42
+ it "flattens arrays" do
43
+ Flatten.([[1, 2, 3], [2, 3]]).should eq([1,2,3,2,3])
44
+ end
45
+
46
+ end
@@ -0,0 +1,34 @@
1
+ require 'functions'
2
+
3
+ include Functions::PreludeMeta
4
+
5
+ describe Functions::PreludeMeta, "basic meta prelude usage" do
6
+
7
+ it "sums" do
8
+ sum([1, 2, 3]).should == 1+2+3
9
+ end
10
+
11
+ it "takes averages" do
12
+ average([2, 3, 8]).should == 4
13
+ end
14
+
15
+ it "folds" do
16
+ foldl(->(n, a) { n/a }, 1.0, [1.0, 2.0, 3.0]).should == 1.0/1.0/2.0/3.0
17
+ foldr(->(n, a) { n/a }, 1.0, [1.0, 2.0, 3.0]).should == 1.0/3.0/2.0/1.0
18
+ end
19
+
20
+ it "mins and maxes" do
21
+ min([1, 2, 3]).should == 1
22
+ max([1, 2, 3]).should == 3
23
+ end
24
+
25
+ it "makes ranges" do
26
+ from_to(2, 3).should == (2..3)
27
+ from_one_to(3).should == (1..3)
28
+ end
29
+
30
+ it "intersects arrays" do
31
+ intersect([[1, 2, 3], [2, 3, 4], [2, 3, 8]]).should == [2, 3]
32
+ end
33
+
34
+ end
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
@@ -0,0 +1,36 @@
1
+ require 'examples/prelude_lambda'
2
+ require 'examples/prelude_meta'
3
+
4
+ #require 'test/unit'
5
+ require 'minitest/autorun'
6
+ require 'minitest/benchmark'
7
+
8
+ #require "minitest/reporters"
9
+ # MiniTest::Reporters.use! # add this to your run configuration
10
+
11
+ include PreludeLambdaUsage
12
+ include PreludeMetaUsage
13
+
14
+ class TestPrelude < MiniTest::Unit::TestCase
15
+
16
+ include Functions::Prelude
17
+
18
+ def test_sum_of_squares
19
+ assert_performance_linear 0.99 do |n| # n is a range value
20
+ 100.times { Sum_Of_Squares.((1..n).to_a) }
21
+ end
22
+ assert_performance_linear 0.99 do |n| # n is a range value
23
+ 100.times { sum_of_squares((1..n).to_a) }
24
+ end
25
+ end
26
+
27
+ def test_average
28
+ assert_performance_linear 0.99 do |n| # n is a range value
29
+ 100.times { Average.((1..n).to_a) }
30
+ end
31
+ assert_performance_linear 0.99 do |n| # n is a range value
32
+ 100.times { average((1..n).to_a) }
33
+ end
34
+ end
35
+
36
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: functions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Koen Handekyn
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-10 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: functions programming in ruby
15
+ email:
16
+ - koen.handekyn@up-nxt.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - .rspec
23
+ - Gemfile
24
+ - LICENSE.txt
25
+ - README.md
26
+ - Rakefile
27
+ - examples/prelude_lambda.rb
28
+ - examples/prelude_meta.rb
29
+ - examples/spec/prelude_lambda_spec.rb
30
+ - examples/spec/prelude_meta_spec.rb
31
+ - functions.gemspec
32
+ - lib/functions.rb
33
+ - lib/functions/prelude_lambda.rb
34
+ - lib/functions/prelude_lambda/basic.rb
35
+ - lib/functions/prelude_lambda/ext.rb
36
+ - lib/functions/prelude_lambda/math.rb
37
+ - lib/functions/prelude_lambda/sorting.rb
38
+ - lib/functions/prelude_meta.rb
39
+ - lib/functions/shared/ext.rb
40
+ - lib/functions/version.rb
41
+ - performance/prelude_performance.rb
42
+ - spec/prelude_lambda_math_spec.rb
43
+ - spec/prelude_lambda_spec.rb
44
+ - spec/prelude_meta_spec.rb
45
+ - spec/spec_helper.rb
46
+ - test/test_prelude_performance.rb
47
+ homepage: http://github.com
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 1.8.24
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: functions programming in ruby
71
+ test_files:
72
+ - spec/prelude_lambda_math_spec.rb
73
+ - spec/prelude_lambda_spec.rb
74
+ - spec/prelude_meta_spec.rb
75
+ - spec/spec_helper.rb
76
+ - test/test_prelude_performance.rb