Skip to content

Instantly share code, notes, and snippets.

@britishtea
Last active December 31, 2015 16:09
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save britishtea/8011845 to your computer and use it in GitHub Desktop.
Save britishtea/8011845 to your computer and use it in GitHub Desktop.
Simple pattern matching in Ruby that won't win any beauty contests.
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"]
# 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