Feeling Objects: Pattern Matching in Ruby
-
Upload
ryan-levick -
Category
Documents
-
view
266 -
download
2
description
Transcript of Feeling Objects: Pattern Matching in Ruby
Friday, April 5, 13
Feel ObjectsPattern Matching in Ruby
Friday, April 5, 13
Name: Ryan Levick
Friday, April 5, 13
Work: 6Wunderkinder
Friday, April 5, 13
Twitter : @itchyankles
Friday, April 5, 13
Friday, April 5, 13
What the hell is pattern matching and why should I
care?
Friday, April 5, 13
Wikipedia Definition
“...the act of checking a perceived sequence of tokens for the presence of the constituents of some pattern.”
Friday, April 5, 13
Wat?
Friday, April 5, 13
My Best Try
Checking a data type against some predefined patterns, and when that data type matches one of the predefined patterns, do something.
Friday, April 5, 13
In Short...
Pattern matching is another way to do control flow.
Friday, April 5, 13
Death to “if-then-else”!
Friday, April 5, 13
Friday, April 5, 13
ein Beispiel
Friday, April 5, 13
fun sum(numbers) = case numbers of
[] => 0x::xs => x + sum(xs)
Friday, April 5, 13
I don’t know SML. What the hell is this?
Friday, April 5, 13
Function declaration
fun sum(numbers) = case numbers of
[] => 0 x::xs => x + sum(xs)
Friday, April 5, 13
Case statement matching against the list “numbers”.
fun sum(numbers) = case numbers of [] => 0 x::xs => x + sum(xs)
Friday, April 5, 13
First pattern
fun sum(numbers) = case numbers of [] => 0 x::xs => x + sum(xs)
Friday, April 5, 13
Second pattern
fun sum(numbers) = case numbers of [] => 0 x::xs => x + sum(xs)
Friday, April 5, 13
So let’s call it!
Friday, April 5, 13
val numbers = [1, 6, 8]
sum(numbers)
Friday, April 5, 13
sum([1, 6, 8]) = case numbers of
[] => 0x::xs => x + sum(xs)
Friday, April 5, 13
What pattern does numbers match?
Friday, April 5, 13
x::xs => x + sum(xs)
1::[6, 8] => 1 + sum([6, 8])
Friday, April 5, 13
x::xs => x + sum(xs)
6::[8] => 1 + 6 + sum([8])
Friday, April 5, 13
x::xs => x + sum(xs)
8::[] => 1 + 6 + 8 + sum([])
Friday, April 5, 13
sum([]) = case numbers of
[] => 0x::xs => x + sum(xs)
Friday, April 5, 13
What pattern does numbers match?
Friday, April 5, 13
[] => 1 + 6 + 8 + 0
15
Friday, April 5, 13
Pretty easy, and could be implemented in roughly the same amount of characters with
“if-then-else”
Friday, April 5, 13
(* Note:
hd(list) takes the first element of list
tl(list) takes all other elements of list
*)
fun sum(numbers) =
if numbers = [] then 0
else hd(numbers) + sum(tl(numbers))
Friday, April 5, 13
Friday, April 5, 13
So maybe you’re not convinced...
Let’s try an example that’s even more interesting with pattern matching.
Friday, April 5, 13
Who knows FizzBuzz?
Friday, April 5, 13
// `~` in Rust is for allocating memory
fn main() { for int::range(1, 101) |num| { io::println( match (num % 3, num % 5) { (0, 0) => ~"FizzBuzz", // must come first (0, _) => ~"Fizz", (_, 0) => ~"Buzz", (_, _) => int::str(num) // must come last } ); }}
// Source: Lindsey Kuper’s FizzBuzz revisited (http://composition.al/blog/2013/03/02/fizzbuzz-revisited/)
Friday, April 5, 13
The real heart of the function:
match (num % 3, num % 5) { (0, 0) => ~"FizzBuzz", // must come first (0, _) => ~"Fizz", (_, 0) => ~"Buzz", (_, _) => int::str(num) // must come last }
Friday, April 5, 13
That’s nice and all, but there’s more...
Friday, April 5, 13
Both SML and Rust are strongly typed, and they let us define our own types.
We can then match against those types.
Friday, April 5, 13
enum Remainder { zero, other(NonZeroRem)}
enum NonZeroRemainder { one, two, three, four}
Friday, April 5, 13
fn int_to_rem(num: int) -> Remainder {
match num { 0 => zero, 1 => other(one), 2 => other(two), 3 => other(three), 4 => other(four), _ => fail }}
Friday, April 5, 13
fn main() { for int::range(1, 101) |num| { io::println( match (int_to_rem(num % 3), int_to_rem(num % 5)) { (other(_), other(_)) => int::str(num), (zero, other(_)) => ~"Fizz", (other(_), zero) => ~"Buzz", (zero, zero) => ~"FizzBuzz" } ); }}
Friday, April 5, 13
Again the heart of the function: match (int_to_rem(num % 3), int_to_rem(num % 5)) { (other(_), other(_)) => int::str(num), (zero, other(_)) => ~"Fizz", (other(_), zero) => ~"Buzz", (zero, zero) => ~"FizzBuzz" }
Friday, April 5, 13
Pattern Matching is powerful.
It allows us to easily change FizzBuzz
Friday, April 5, 13
match (int_to_rem(num % 3), int_to_rem(num % 5)) { (other(two), other(one)) => ~"Zot", (other(x), other(y)) => int::str(x/y), (zero, other(_)) => ~"Fizz", (other(x), zero) => ~"Buzz" + int::str(x), (zero, zero) => ~"FizzBuzz" }
Friday, April 5, 13
Friday, April 5, 13
So how about Pattern Matching in Ruby?
Friday, April 5, 13
pattern-match
github.com/k-tsj/pattern-match
Friday, April 5, 13
require 'pattern-match'
match(object) do with(pattern) do ... end with(pattern) do ... end ...end
# patterns are binded variables available in block
Friday, April 5, 13
x = match(0) do with(String) { “It’s a String!” } with(Fixnum) { “It’s a Fixnum!” } end
print x #=> “It’s a Fixnum!”
Friday, April 5, 13
require 'pattern-match'
1.upto(100) do |n|
match([(n % 3),(n % 5)]) do
with(_[0,0]) { puts "FizzBuzz" }
with(_[0,_]) { puts "Fizz" }
with(_[_,0]) { puts "Buzz" }
with(_[_,_]) { puts n }
end
end
Friday, April 5, 13
It allows for some cool things!
Friday, April 5, 13
require 'pattern-match'
match([1, "2", 3.0, "four"]) do
with(_[a & 1, b & Or(Float, String), c & Not(Fixnum), d])
with(_[a & Fixnum, b & Not(String), c, d & Not(“four”)])
end
end
Friday, April 5, 13
Friday, April 5, 13