multi 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/amulti.rb +40 -0
  2. data/multi.rb +139 -0
  3. data/smulti.rb +56 -0
  4. metadata +47 -0
@@ -0,0 +1,40 @@
1
+ # = amulti.rb - Array destructuring multiple dispatch for Ruby
2
+ #
3
+ # Copyright 2005, Christopher Cyll
4
+ # mailto: christopher at gmail dot com
5
+ #
6
+ # == Example
7
+ #
8
+ # === Array Dispatch (using 'amulti') ===
9
+ #
10
+
11
+ require 'multi'
12
+
13
+ def amulti(method_name, *patterns, &body)
14
+ Multi::DISPATCHER.add(Multi::ArrayDispatch, self, method_name, patterns, body)
15
+ end
16
+
17
+ module Multi
18
+ class ArrayDispatch < Dispatch
19
+ def initialize(patterns, body)
20
+ @count = patterns.size
21
+ super(patterns, body)
22
+ end
23
+
24
+ def match?(params)
25
+ return false if params.size != 1
26
+ # Call .to_a here?
27
+ array = params.first
28
+ return false if ! array.kind_of?(Array)
29
+ return false if array.size < @count
30
+ return super(array[0, @count])
31
+ end
32
+
33
+ def call(params, block)
34
+ array = params.first
35
+ use = array[0, @count]
36
+ use.push(array[@count..-1])
37
+ super(use, block)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,139 @@
1
+ # = multi.rb - Multiple Dispatch and Pattern Matching for Ruby
2
+ #
3
+ # Copyright 2005, Christopher Cyll
4
+ # mailto: christopher at gmail dot com
5
+ #
6
+ # == Example
7
+ #
8
+ # === Factorial Function
9
+ #
10
+ # require 'multi'
11
+ # multi(:fac, 0) { 1 }
12
+ # multi(:fac, Integer) {|x| x * fac(x-1)}
13
+ # fac(5) ==> 120
14
+ #
15
+ # === List Reversal Function
16
+ #
17
+ # require 'multi'
18
+ # multi(:reverse, []) { [] }
19
+ # multi(:reverse, Array) {|list| [list.pop] + reverse(list) }
20
+ # reverse([1,2,3]) ==> [3,2,1]
21
+ #
22
+ # === Method Dispatch
23
+ #
24
+ # require 'multi'
25
+ # class Foo
26
+ # multi(:hiya, 0) {|x| "Zero: #{x}" }
27
+ # multi(:hiya, Integer) {|x| "Int: #{x}" }
28
+ # multi(:hiya, String) {|x| "Str: #{x}" }
29
+ # end
30
+ #
31
+ # f = Foo.new()
32
+ # f.hiya(0) ==> "Zero: 0"
33
+ # f.hiya(5) ==> "Int: 5"
34
+ # f.hiya("hello") ==> "Str: hello"
35
+ #
36
+ # === Match Any ( _ in Haskell/ML) using Object
37
+ #
38
+ # require 'multi'
39
+ # multi(:baz, 3, Object) { 3 }
40
+ # multi(:baz, Object, String) {|o, str| str }
41
+ # baz(3, "three") ==> 3
42
+ # baz(2, "two") ==> "two"
43
+ #
44
+ # === Guards Using lambda/Proc
45
+ #
46
+ # multi(:gt2, lambda {|x| x > 2 }) {|x| x }
47
+ # multi(:gt2, Object) { 0 }
48
+ # gt2(1) ==> 0
49
+ # gt2(4) ==> 4
50
+ #
51
+ # === Returning Values From Multimethods
52
+ #
53
+ # multi(:evenify, Integer) do |x|
54
+ # next x if x % 2 == 0 # Return x using 'next'
55
+ # x += 1
56
+ # next x
57
+ # end
58
+ # evenify(4) ==> 4
59
+ # evenify(5) ==> 6
60
+ #
61
+ # === Declare a clause of a multimethod
62
+ #
63
+ # multi(:method_name, types_literals_or_guards) do |parameter1, parameter2|
64
+ # # code here!
65
+ # end
66
+
67
+ def multi(method_name, *patterns, &body)
68
+ Multi::DISPATCHER.add(Multi::Dispatch, self, method_name, patterns, body)
69
+ end
70
+
71
+ module Multi
72
+ class Dispatch
73
+ def initialize(patterns, body)
74
+ @patterns = patterns
75
+ @body = body
76
+ end
77
+
78
+ def match?(params)
79
+ pairs = params.zip(@patterns)
80
+ return pairs.all? do |param, pattern|
81
+ if pattern.kind_of?(Class)
82
+ param.kind_of?(pattern)
83
+ elsif pattern.instance_of?(Proc)
84
+ begin
85
+ pattern.call(param)
86
+ rescue
87
+ false
88
+ end
89
+ elsif pattern.instance_of?(Regexp)
90
+ pattern.match(param)
91
+ else
92
+ param == pattern
93
+ end
94
+ end
95
+ end
96
+
97
+ def call(params, block)
98
+ @body.call(*params, &block)
99
+ end
100
+ end
101
+
102
+ class Dispatcher
103
+ def initialize
104
+ @map = {}
105
+ end
106
+
107
+ def add(type, obj, method_name, patterns, body)
108
+ method_name = method_name.id2name if method_name.kind_of?(Symbol)
109
+ body = patterns.pop if body.nil?
110
+
111
+ # Using the object_id() is pretty sleazy, but it gives us faster
112
+ # lookup than object.equal? and searching
113
+ key = [obj.object_id(), method_name]
114
+ @map[key] ||= []
115
+ @map[key].push(type.new(patterns, body))
116
+
117
+ # Tried to use send(:define, ...) but Procs can't have &blocks
118
+ if ! obj.methods.include?(method_name)
119
+ obj.instance_eval <<-"DONE"
120
+ def #{method_name}(*params, &block)
121
+ Multi::DISPATCHER.call(self, \"#{method_name}\", params, block)
122
+ end
123
+ DONE
124
+ end
125
+ end
126
+
127
+ def call(obj, method_name, params, block)
128
+ dispatches = @map[[obj.object_id, method_name]]
129
+ dispatch = dispatches.find{|dispatch| dispatch.match?(params) }
130
+ if dispatch.nil?
131
+ printed_params = params.map{|param| param.inspect}.join(', ')
132
+ raise "No match for #{obj}.#{method_name}(#{printed_params})"
133
+ end
134
+ dispatch.call(params, block)
135
+ end
136
+ end
137
+
138
+ DISPATCHER = Dispatcher.new()
139
+ end
@@ -0,0 +1,56 @@
1
+ # = smulti.rb - String destructuring multiple dispatch for Ruby
2
+ #
3
+ # Copyright 2005, Christopher Cyll
4
+ # mailto: christopher at gmail dot com
5
+ #
6
+ # == Example
7
+ #
8
+ # === String Dispatch (using 'smulti') ===
9
+ #
10
+ # smulti(:foo, 'a') { puts "a FOUND" }
11
+ # smulti(:foo, /./) {|s, rest| foo(rest) }
12
+ # smulti(:foo, // ) { puts "a NOT FOUND" }
13
+ # foo('a') ==> "a FOUND"
14
+ # foo('') ==> "a NOT FOUND"
15
+ # foo('ab') ==> "a FOUND"
16
+ # foo('ba') ==> "a FOUND"
17
+ # foo('bb') ==> "a NOT FOUND"
18
+
19
+ require 'multi'
20
+
21
+ def smulti(method_name, *patterns, &body)
22
+ Multi::DISPATCHER.add(Multi::StringDispatch, self, method_name, patterns, body)
23
+ end
24
+
25
+ module Multi
26
+ class StringDispatch
27
+ def initialize(patterns, body)
28
+ @body = body
29
+
30
+ pattern = patterns.first
31
+ if pattern.kind_of?(String)
32
+ source = "\\A" + Regexp.escape(pattern)
33
+ @re = Regexp.new(source)
34
+ elsif pattern.kind_of?(Regexp)
35
+ opts = pattern.options
36
+ source = "\\A" + pattern.source
37
+ @re = Regexp.new(source, opts)
38
+ else
39
+ throw "Bad pattern #{pattern} for StringDispatch. Use a string or a regexp"
40
+ end
41
+ end
42
+
43
+ def match?(params)
44
+ return false if params.size != 1
45
+ @re.match(params.first)
46
+ end
47
+
48
+ def call(params, block)
49
+ match = @re.match(params.first)
50
+ results = match.captures
51
+ results.push(match[0]) if results.empty?
52
+ results.push(match.post_match)
53
+ @body.call(*results, &block)
54
+ end
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: multi
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2006-01-03 00:00:00 -08:00
8
+ summary: Multiple Dispatch/Pattern Matching for Ruby
9
+ require_paths:
10
+ - .
11
+ email: christophercyll@gmail.com
12
+ homepage: http://cyll.org/multi
13
+ rubyforge_project: multi
14
+ description:
15
+ autorequire: multi
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - Topher Cyll
30
+ files:
31
+ - multi.rb
32
+ - smulti.rb
33
+ - amulti.rb
34
+ test_files: []
35
+
36
+ rdoc_options: []
37
+
38
+ extra_rdoc_files: []
39
+
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ requirements: []
45
+
46
+ dependencies: []
47
+