functional-ruby 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,2 +1,2 @@
1
- # Support the Scandinavian spelling
2
- require_relative 'behavior'
1
+ # Support the Scandinavian spelling
2
+ require_relative 'behavior'
@@ -1,133 +1,133 @@
1
- module PatternMatching
2
-
3
- UNBOUND = Class.new
4
- ALL = Class.new
5
-
6
- private
7
-
8
- GUARD_CLAUSE = Class.new do # :nodoc:
9
- def initialize(func, clazz, matcher) # :nodoc:
10
- @func = func
11
- @clazz = clazz
12
- @matcher = matcher
13
- end
14
- def when(&block) # :nodoc:
15
- unless block_given?
16
- raise ArgumentError.new("block missing for `when` guard on function `#{@func}` of class #{@clazz}")
17
- end
18
- @matcher[@matcher.length-1] = block
19
- return nil
20
- end
21
- end
22
-
23
- def self.__match_pattern__(args, pattern) # :nodoc:
24
- return unless (pattern.last == ALL && args.length >= pattern.length) \
25
- || (args.length == pattern.length)
26
- pattern.each_with_index do |p, i|
27
- break if p == ALL && i+1 == pattern.length
28
- arg = args[i]
29
- next if p.is_a?(Class) && arg.is_a?(p)
30
- if p.is_a?(Hash) && arg.is_a?(Hash) && ! p.empty?
31
- p.each do |key, value|
32
- return false unless arg.has_key?(key)
33
- next if value == UNBOUND
34
- return false unless arg[key] == value
35
- end
36
- next
37
- end
38
- return false unless p == UNBOUND || p == arg
39
- end
40
- return true
41
- end
42
-
43
- def self.__unbound_args__(match, args) # :nodoc:
44
- argv = []
45
- match.first.each_with_index do |p, i|
46
- if p == ALL && i == match.first.length-1
47
- argv << args[(i..args.length)].reduce([]){|memo, arg| memo << arg }
48
- elsif p.is_a?(Hash) && p.values.include?(UNBOUND)
49
- p.each do |key, value|
50
- argv << args[i][key] if value == UNBOUND
51
- end
52
- elsif p.is_a?(Hash) || p == UNBOUND || p.is_a?(Class)
53
- argv << args[i]
54
- end
55
- end
56
- return argv
57
- end
58
-
59
- def self.__pattern_match__(clazz, func, *args, &block) # :nodoc:
60
- args = args.first
61
-
62
- matchers = clazz.__function_pattern_matches__[func]
63
- return [:nodef, nil] if matchers.nil?
64
-
65
- match = matchers.detect do |matcher|
66
- if PatternMatching.__match_pattern__(args, matcher.first)
67
- if matcher.last.nil?
68
- true # no guard clause
69
- else
70
- self.instance_exec(*PatternMatching.__unbound_args__(matcher, args), &matcher.last)
71
- end
72
- end
73
- end
74
-
75
- return (match ? [:ok, match] : [:nomatch, nil])
76
- end
77
-
78
- protected
79
-
80
- def self.included(base)
81
-
82
- class << base
83
-
84
- public
85
-
86
- def _() # :nodoc:
87
- return UNBOUND
88
- end
89
-
90
- def defn(func, *args, &block)
91
- unless block_given?
92
- raise ArgumentError.new("block missing for definition of function `#{func}` on class #{self}")
93
- end
94
-
95
- pattern = __add_pattern_for__(func, *args, &block)
96
-
97
- unless self.instance_methods(false).include?(func)
98
-
99
- define_method(func) do |*args, &block|
100
- result, match = PatternMatching.__pattern_match__(self.method(func).owner, func, args, block)
101
- if result == :ok
102
- # if a match is found call the block
103
- argv = PatternMatching.__unbound_args__(match, args)
104
- return self.instance_exec(*argv, &match[1])
105
- else # if result == :nodef || result == :nomatch
106
- begin
107
- super(*args, &block)
108
- rescue NoMethodError, ArgumentError
109
- raise NoMethodError.new("no method `#{func}` matching #{args} found for class #{self.class}")
110
- end
111
- end
112
- end
113
- end
114
-
115
- return GUARD_CLAUSE.new(func, self, pattern)
116
- end
117
-
118
- public
119
-
120
- def __function_pattern_matches__ # :nodoc:
121
- @__function_pattern_matches__ ||= Hash.new
122
- end
123
-
124
- def __add_pattern_for__(func, *args, &block) # :nodoc:
125
- block = Proc.new{} unless block_given?
126
- matchers = self.__function_pattern_matches__
127
- matchers[func] = [] unless matchers.has_key?(func)
128
- matchers[func] << [args, block, nil]
129
- return matchers[func].last
130
- end
131
- end
132
- end
133
- end
1
+ module PatternMatching
2
+
3
+ UNBOUND = Class.new
4
+ ALL = Class.new
5
+
6
+ private
7
+
8
+ GUARD_CLAUSE = Class.new do # :nodoc:
9
+ def initialize(func, clazz, matcher) # :nodoc:
10
+ @func = func
11
+ @clazz = clazz
12
+ @matcher = matcher
13
+ end
14
+ def when(&block) # :nodoc:
15
+ unless block_given?
16
+ raise ArgumentError.new("block missing for `when` guard on function `#{@func}` of class #{@clazz}")
17
+ end
18
+ @matcher[@matcher.length-1] = block
19
+ return nil
20
+ end
21
+ end
22
+
23
+ def self.__match_pattern__(args, pattern) # :nodoc:
24
+ return unless (pattern.last == ALL && args.length >= pattern.length) \
25
+ || (args.length == pattern.length)
26
+ pattern.each_with_index do |p, i|
27
+ break if p == ALL && i+1 == pattern.length
28
+ arg = args[i]
29
+ next if p.is_a?(Class) && arg.is_a?(p)
30
+ if p.is_a?(Hash) && arg.is_a?(Hash) && ! p.empty?
31
+ p.each do |key, value|
32
+ return false unless arg.has_key?(key)
33
+ next if value == UNBOUND
34
+ return false unless arg[key] == value
35
+ end
36
+ next
37
+ end
38
+ return false unless p == UNBOUND || p == arg
39
+ end
40
+ return true
41
+ end
42
+
43
+ def self.__unbound_args__(match, args) # :nodoc:
44
+ argv = []
45
+ match.first.each_with_index do |p, i|
46
+ if p == ALL && i == match.first.length-1
47
+ argv << args[(i..args.length)].reduce([]){|memo, arg| memo << arg }
48
+ elsif p.is_a?(Hash) && p.values.include?(UNBOUND)
49
+ p.each do |key, value|
50
+ argv << args[i][key] if value == UNBOUND
51
+ end
52
+ elsif p.is_a?(Hash) || p == UNBOUND || p.is_a?(Class)
53
+ argv << args[i]
54
+ end
55
+ end
56
+ return argv
57
+ end
58
+
59
+ def self.__pattern_match__(clazz, func, *args, &block) # :nodoc:
60
+ args = args.first
61
+
62
+ matchers = clazz.__function_pattern_matches__[func]
63
+ return [:nodef, nil] if matchers.nil?
64
+
65
+ match = matchers.detect do |matcher|
66
+ if PatternMatching.__match_pattern__(args, matcher.first)
67
+ if matcher.last.nil?
68
+ true # no guard clause
69
+ else
70
+ self.instance_exec(*PatternMatching.__unbound_args__(matcher, args), &matcher.last)
71
+ end
72
+ end
73
+ end
74
+
75
+ return (match ? [:ok, match] : [:nomatch, nil])
76
+ end
77
+
78
+ protected
79
+
80
+ def self.included(base)
81
+
82
+ class << base
83
+
84
+ public
85
+
86
+ def _() # :nodoc:
87
+ return UNBOUND
88
+ end
89
+
90
+ def defn(func, *args, &block)
91
+ unless block_given?
92
+ raise ArgumentError.new("block missing for definition of function `#{func}` on class #{self}")
93
+ end
94
+
95
+ pattern = __add_pattern_for__(func, *args, &block)
96
+
97
+ unless self.instance_methods(false).include?(func)
98
+
99
+ define_method(func) do |*args, &block|
100
+ result, match = PatternMatching.__pattern_match__(self.method(func).owner, func, args, block)
101
+ if result == :ok
102
+ # if a match is found call the block
103
+ argv = PatternMatching.__unbound_args__(match, args)
104
+ return self.instance_exec(*argv, &match[1])
105
+ else # if result == :nodef || result == :nomatch
106
+ begin
107
+ super(*args, &block)
108
+ rescue NoMethodError, ArgumentError
109
+ raise NoMethodError.new("no method `#{func}` matching #{args} found for class #{self.class}")
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ return GUARD_CLAUSE.new(func, self, pattern)
116
+ end
117
+
118
+ public
119
+
120
+ def __function_pattern_matches__ # :nodoc:
121
+ @__function_pattern_matches__ ||= Hash.new
122
+ end
123
+
124
+ def __add_pattern_for__(func, *args, &block) # :nodoc:
125
+ block = Proc.new{} unless block_given?
126
+ matchers = self.__function_pattern_matches__
127
+ matchers[func] = [] unless matchers.has_key?(func)
128
+ matchers[func] << [args, block, nil]
129
+ return matchers[func].last
130
+ end
131
+ end
132
+ end
133
+ end
@@ -1,192 +1,192 @@
1
- require 'pp'
2
- require 'stringio'
3
- require 'erb'
4
- require 'rbconfig'
5
-
6
- Infinity = 1/0.0 unless defined?(Infinity)
7
- NaN = 0/0.0 unless defined?(NaN)
8
-
9
- module Kernel
10
-
11
- # Compute the difference (delta) between two values.
12
- #
13
- # When a block is given the block will be applied to both arguments.
14
- # Using a block in this way allows computation against a specific field
15
- # in a data set of hashes or objects.
16
- #
17
- # @yield iterates over each element in the data set
18
- # @yieldparam item each element in the data set
19
- #
20
- # @param [Object] v1 the first value
21
- # @param [Object] v2 the second value
22
- #
23
- # @return [Float] positive value representing the difference
24
- # between the two parameters
25
- def delta(v1, v2)
26
- if block_given?
27
- v1 = yield(v1)
28
- v2 = yield(v2)
29
- end
30
- return (v1 - v2).abs
31
- end
32
- module_function :delta
33
-
34
- # Perform an operation numerous times, passing the value of the
35
- # previous iteration, and collecting the results into an array.
36
- #
37
- # @yield iterates over each element in the data set
38
- # @yieldparam previous the initial value (or nil) for the first
39
- # iteration then the value of the previous iteration for all
40
- # subsequent iterations
41
- #
42
- # @param [Integer] count the number of times to perform the operation
43
- # @param [Object] initial the initial value to pass to the first iteration
44
- #
45
- # @return [Array] the results of the iterations collected into an array
46
- def repeatedly(count, initial = nil)
47
- return [] if (count = count.to_i) == 0
48
- return count.times.collect{ nil } unless block_given?
49
- return count.times.collect do
50
- initial = yield(initial)
51
- end
52
- end
53
- module_function :repeatedly
54
-
55
- # Try an operation. If it fails (raises an exception), wait a second
56
- # and try again. Try no more than the given number of times.
57
- #
58
- # @yield Tries the given block operation
59
- #
60
- # @param [Integer] tries The maximum number of times to attempt the operation.
61
- # @param [Array] args Optional block arguments
62
- #
63
- # @return [Boolean] true if the operation succeeds on any attempt,
64
- # false if none of the attempts are successful
65
- def retro(tries, *args)
66
- tries = tries.to_i
67
- return false if tries == 0 || ! block_given?
68
- yield(*args)
69
- return true
70
- rescue Exception
71
- sleep(1)
72
- if (tries = tries - 1) > 0
73
- retry
74
- else
75
- return false
76
- end
77
- end
78
- module_function :retro
79
-
80
- # Sandbox the given operation at a high $SAFE level.
81
- #
82
- # @param args [Array] zero or more arguments to pass to the block
83
- #
84
- # @return [Object] the result of the block operation
85
- def safe(*args)
86
- raise ArgumentError.new('no block given') unless block_given?
87
- if RbConfig::CONFIG['ruby_install_name'] =~ /^ruby$/i
88
- result = nil
89
- t = Thread.new do
90
- $SAFE = 3
91
- result = yield(*args)
92
- end
93
- t.join
94
- return result
95
- else
96
- return yield(*args)
97
- end
98
- end
99
- module_function :safe
100
-
101
- # Open a file, read it, close the file, and return its contents.
102
- #
103
- # @param file [String] path to and name of the file to open
104
- #
105
- # @return [String] file contents
106
- #
107
- # @see slurpee
108
- def slurp(file)
109
- File.open(file, 'rb') {|f| f.read }
110
- end
111
- module_function :slurp
112
-
113
- # Open a file, read it, close the file, run the contents through the
114
- # ERB parser, and return updated contents.
115
- #
116
- # @param file [String] path to and name of the file to open
117
- # @param safe_level [Integer] when not nil, ERB will $SAFE set to this
118
- #
119
- # @return [String] file contents
120
- #
121
- # @see slurpee
122
- def slurpee(file, safe_level = nil)
123
- ERB.new(slurp(file), safe_level).result
124
- end
125
- module_function :slurpee
126
-
127
- # Run the given block and time how long it takes in seconds. All arguments
128
- # will be passed to the block. The function will return two values. The
129
- # first value will be the duration of the timer in seconds. The second
130
- # return value will be the result of the block.
131
- #
132
- # @param args [Array] zero or more arguments to pass to the block
133
- #
134
- # @return [Integer, Object] the duration of the operation in seconds and
135
- # the result of the block operation
136
- def timer(*args)
137
- return 0,nil unless block_given?
138
- t1 = Time.now
139
- result = yield(*args)
140
- t2 = Time.now
141
- return (t2 - t1), result
142
- end
143
- module_function :timer
144
-
145
- #############################################################################
146
-
147
- # @private
148
- # @see http://cirw.in/blog/find-references
149
- def object_counts # :nodoc:
150
- counts = Hash.new{ 0 }
151
- ObjectSpace.each_object do |obj|
152
- counts[obj.class] += 1
153
- end
154
- return counts
155
- end
156
- module_function :object_counts
157
-
158
- # @private
159
- # @see http://rhaseventh.blogspot.com/2008/07/ruby-and-rails-how-to-get-pp-pretty.html
160
- def pp_s(*objs) # :nodoc:
161
- s = StringIO.new
162
- objs.each {|obj|
163
- PP.pp(obj, s)
164
- }
165
- s.rewind
166
- s.read
167
- end
168
- module_function :pp_s
169
-
170
- # @private
171
- def repl? # :nodoc:
172
- return ($0 == 'irb' || $0 == 'pry' || $0 == 'script/rails' || !!($0 =~ /bin\/bundle$/))
173
- end
174
- module_function :repl?
175
-
176
- # @private
177
- def strftimer(seconds) # :nodoc:
178
- Time.at(seconds).gmtime.strftime('%R:%S.%L')
179
- end
180
- module_function :strftimer
181
-
182
- # @private
183
- def timestamp # :nodoc:
184
- return Time.now.getutc.to_i
185
- end
186
-
187
- def write_object_counts(name = 'ruby')
188
- file = "#{name}_#{Time.now.to_i}.txt"
189
- File.open(file, 'w') {|f| f.write(pp_s(object_counts)) }
190
- end
191
- module_function :write_object_counts
192
- end
1
+ require 'pp'
2
+ require 'stringio'
3
+ require 'erb'
4
+ require 'rbconfig'
5
+
6
+ Infinity = 1/0.0 unless defined?(Infinity)
7
+ NaN = 0/0.0 unless defined?(NaN)
8
+
9
+ module Kernel
10
+
11
+ # Compute the difference (delta) between two values.
12
+ #
13
+ # When a block is given the block will be applied to both arguments.
14
+ # Using a block in this way allows computation against a specific field
15
+ # in a data set of hashes or objects.
16
+ #
17
+ # @yield iterates over each element in the data set
18
+ # @yieldparam item each element in the data set
19
+ #
20
+ # @param [Object] v1 the first value
21
+ # @param [Object] v2 the second value
22
+ #
23
+ # @return [Float] positive value representing the difference
24
+ # between the two parameters
25
+ def delta(v1, v2)
26
+ if block_given?
27
+ v1 = yield(v1)
28
+ v2 = yield(v2)
29
+ end
30
+ return (v1 - v2).abs
31
+ end
32
+ module_function :delta
33
+
34
+ # Perform an operation numerous times, passing the value of the
35
+ # previous iteration, and collecting the results into an array.
36
+ #
37
+ # @yield iterates over each element in the data set
38
+ # @yieldparam previous the initial value (or nil) for the first
39
+ # iteration then the value of the previous iteration for all
40
+ # subsequent iterations
41
+ #
42
+ # @param [Integer] count the number of times to perform the operation
43
+ # @param [Object] initial the initial value to pass to the first iteration
44
+ #
45
+ # @return [Array] the results of the iterations collected into an array
46
+ def repeatedly(count, initial = nil)
47
+ return [] if (count = count.to_i) == 0
48
+ return count.times.collect{ nil } unless block_given?
49
+ return count.times.collect do
50
+ initial = yield(initial)
51
+ end
52
+ end
53
+ module_function :repeatedly
54
+
55
+ # Try an operation. If it fails (raises an exception), wait a second
56
+ # and try again. Try no more than the given number of times.
57
+ #
58
+ # @yield Tries the given block operation
59
+ #
60
+ # @param [Integer] tries The maximum number of times to attempt the operation.
61
+ # @param [Array] args Optional block arguments
62
+ #
63
+ # @return [Boolean] true if the operation succeeds on any attempt,
64
+ # false if none of the attempts are successful
65
+ def retro(tries, *args)
66
+ tries = tries.to_i
67
+ return false if tries == 0 || ! block_given?
68
+ yield(*args)
69
+ return true
70
+ rescue Exception
71
+ sleep(1)
72
+ if (tries = tries - 1) > 0
73
+ retry
74
+ else
75
+ return false
76
+ end
77
+ end
78
+ module_function :retro
79
+
80
+ # Sandbox the given operation at a high $SAFE level.
81
+ #
82
+ # @param args [Array] zero or more arguments to pass to the block
83
+ #
84
+ # @return [Object] the result of the block operation
85
+ def safe(*args)
86
+ raise ArgumentError.new('no block given') unless block_given?
87
+ if RbConfig::CONFIG['ruby_install_name'] =~ /^ruby$/i
88
+ result = nil
89
+ t = Thread.new do
90
+ $SAFE = 3
91
+ result = yield(*args)
92
+ end
93
+ t.join
94
+ return result
95
+ else
96
+ return yield(*args)
97
+ end
98
+ end
99
+ module_function :safe
100
+
101
+ # Open a file, read it, close the file, and return its contents.
102
+ #
103
+ # @param file [String] path to and name of the file to open
104
+ #
105
+ # @return [String] file contents
106
+ #
107
+ # @see slurpee
108
+ def slurp(file)
109
+ File.open(file, 'rb') {|f| f.read }
110
+ end
111
+ module_function :slurp
112
+
113
+ # Open a file, read it, close the file, run the contents through the
114
+ # ERB parser, and return updated contents.
115
+ #
116
+ # @param file [String] path to and name of the file to open
117
+ # @param safe_level [Integer] when not nil, ERB will $SAFE set to this
118
+ #
119
+ # @return [String] file contents
120
+ #
121
+ # @see slurpee
122
+ def slurpee(file, safe_level = nil)
123
+ ERB.new(slurp(file), safe_level).result
124
+ end
125
+ module_function :slurpee
126
+
127
+ # Run the given block and time how long it takes in seconds. All arguments
128
+ # will be passed to the block. The function will return two values. The
129
+ # first value will be the duration of the timer in seconds. The second
130
+ # return value will be the result of the block.
131
+ #
132
+ # @param args [Array] zero or more arguments to pass to the block
133
+ #
134
+ # @return [Integer, Object] the duration of the operation in seconds and
135
+ # the result of the block operation
136
+ def timer(*args)
137
+ return 0,nil unless block_given?
138
+ t1 = Time.now
139
+ result = yield(*args)
140
+ t2 = Time.now
141
+ return (t2 - t1), result
142
+ end
143
+ module_function :timer
144
+
145
+ #############################################################################
146
+
147
+ # @private
148
+ # @see http://cirw.in/blog/find-references
149
+ def object_counts # :nodoc:
150
+ counts = Hash.new{ 0 }
151
+ ObjectSpace.each_object do |obj|
152
+ counts[obj.class] += 1
153
+ end
154
+ return counts
155
+ end
156
+ module_function :object_counts
157
+
158
+ # @private
159
+ # @see http://rhaseventh.blogspot.com/2008/07/ruby-and-rails-how-to-get-pp-pretty.html
160
+ def pp_s(*objs) # :nodoc:
161
+ s = StringIO.new
162
+ objs.each {|obj|
163
+ PP.pp(obj, s)
164
+ }
165
+ s.rewind
166
+ s.read
167
+ end
168
+ module_function :pp_s
169
+
170
+ # @private
171
+ def repl? # :nodoc:
172
+ return ($0 == 'irb' || $0 == 'pry' || $0 == 'script/rails' || !!($0 =~ /bin\/bundle$/))
173
+ end
174
+ module_function :repl?
175
+
176
+ # @private
177
+ def strftimer(seconds) # :nodoc:
178
+ Time.at(seconds).gmtime.strftime('%R:%S.%L')
179
+ end
180
+ module_function :strftimer
181
+
182
+ # @private
183
+ def timestamp # :nodoc:
184
+ return Time.now.getutc.to_i
185
+ end
186
+
187
+ def write_object_counts(name = 'ruby')
188
+ file = "#{name}_#{Time.now.to_i}.txt"
189
+ File.open(file, 'w') {|f| f.write(pp_s(object_counts)) }
190
+ end
191
+ module_function :write_object_counts
192
+ end