Last active

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

Simple pattern matching in Ruby that won't win any beauty contests.

View example.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
require 'function'
 
# A fibonacci function.
 
fib = Function.new
fib[0] = 0
fib[1] = 1
fib[Integer] = ->(i) { fib[i - 2] + fib[i - 1] }
 
p fib[0] # => 0
p fib[20] # => 6765
 
fib["a"] rescue p $!
# => #<Function::TypeError: No matching pattern for "a".>
 
 
 
# An implementation of map.
 
map = Function.new
map[[], Function] = []
map[Array, Function] = ->(arr, f) { [f[arr[0]]] + map[arr[1..-1], f] }
 
p map[[0,1,2,5,20], fib]
p map[[0,1,2,5,20], fib]
# => [0, 1, 1, 5, 6765]
 
 
 
# Let's do some guarding. It ain't pretty.
 
fizzbuzz = Function.new
fizzbuzz[->(i) { i % 3 == 0 && i % 5 == 0 }] = "fizzbuzz"
fizzbuzz[->(i) { i % 3 == 0 }] = "fizz"
fizzbuzz[->(i) { i % 5 == 0 }] = "buzz"
fizzbuzz[:_] = ->(i) { i }
 
p map[1.upto(15).to_a, fizzbuzz]
# => [1, 2, "fizz", 4, "buzz", "fizz", 7, 8, "fizz", "buzz", 11, "fizz", 13, 14, "fizzbuzz"]
View example.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
# A simple function with pattern matching.
class Function
WILDCARD = :_
TypeError = Class.new TypeError
 
def initialize(&block)
@patterns = Hash.new
end
 
# Public: Defines behaviour for a pattern.
def []=(*pattern, value)
@patterns[pattern] = value
end
 
# Public: Calls the function.
#
# Raises a Function::TypeError when no matching pattern was found.
def [](*args)
key, value = find_exact_match(args) || find_pattern_match(args)
 
if key.nil?
raise TypeError, "No matching pattern for #{args.inspect[1..-2]}."
end
if value.is_a? Proc
return value.call(*args)
else
return value
end
end
 
alias_method :call, :[]
 
def to_proc
proc { |*args| self[*args] }
end
 
private
# A set of values that are equal (#==) to another set of values.
# Example: [1,2,3] and [1,2,3]
def find_exact_match(arguments)
@patterns.find { |pattern, _| arguments == pattern }
end
 
# A set of values that are threequal (#===) to another set of values. Note
# that :_ is treated as a wildcard.
# Example: [Integer, Integer, :_] and [1,2,3].
def find_pattern_match(arguments)
@patterns.find do |pattern, _|
pattern.zip(arguments).all? do |one, two|
[ one == WILDCARD, # This is a wildcard.
one === two, # This executes Procs, we've got guards! :D
one == two
].any?
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.