multi 0.1
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/amulti.rb +40 -0
- data/multi.rb +139 -0
- data/smulti.rb +56 -0
- metadata +47 -0
data/amulti.rb
ADDED
@@ -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
|
data/multi.rb
ADDED
@@ -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
|
data/smulti.rb
ADDED
@@ -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
|
+
|