mirror of
https://github.com/edgecase/ruby_koans.git
synced 2026-04-13 06:43:20 -04:00
Merge pull request #17 from Wolwer1nE/pattern_matching
Added pattern matching koans
This commit is contained in:
215
src/about_pattern_matching.rb
Normal file
215
src/about_pattern_matching.rb
Normal file
@@ -0,0 +1,215 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/neo')
|
||||
|
||||
class AboutPatternMatching < Neo::Koan
|
||||
|
||||
def test_pattern_may_not_match
|
||||
begin
|
||||
case [true, false]
|
||||
in [a, b] if a == b # The condition after pattern is called guard.
|
||||
:match
|
||||
end
|
||||
rescue Exception => ex
|
||||
# What exception has been caught?
|
||||
assert_equal __, ex.class
|
||||
end
|
||||
end
|
||||
|
||||
def test_we_can_use_else
|
||||
result = case [true, false]
|
||||
in [a, b] if a == b
|
||||
:match
|
||||
else
|
||||
:no_match
|
||||
end
|
||||
|
||||
assert_equal __, result
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def value_pattern(variable)
|
||||
case variable
|
||||
in 0
|
||||
:match_exact_value
|
||||
in 1..10
|
||||
:match_in_range
|
||||
in Integer
|
||||
:match_with_class
|
||||
else
|
||||
:no_match
|
||||
end
|
||||
end
|
||||
|
||||
def test_value_pattern
|
||||
assert_equal __, value_pattern(0)
|
||||
assert_equal __, value_pattern(5)
|
||||
assert_equal __, value_pattern(100)
|
||||
assert_equal __, value_pattern('Not a Number!')
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# This pattern will bind variable to the value
|
||||
|
||||
def variable_pattern_with_binding(variable)
|
||||
case 0
|
||||
in variable
|
||||
variable
|
||||
else
|
||||
:no_match
|
||||
end
|
||||
end
|
||||
|
||||
def test_variable_pattern_with_binding
|
||||
assert_equal __, variable_pattern_with_binding(1)
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# We can pin the value of the variable with ^
|
||||
|
||||
def variable_pattern_with_pin(variable)
|
||||
case 0
|
||||
in ^variable
|
||||
variable
|
||||
else
|
||||
:no_match
|
||||
end
|
||||
end
|
||||
|
||||
def test_variable_pattern_with_pin
|
||||
assert_equal __, variable_pattern_with_pin(1)
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# We can drop values from pattern
|
||||
|
||||
def pattern_with_dropping(variable)
|
||||
case variable
|
||||
in [_, 2]
|
||||
:match
|
||||
else
|
||||
:no_match
|
||||
end
|
||||
end
|
||||
|
||||
def test_pattern_with_dropping
|
||||
assert_equal __, pattern_with_dropping(['I will not be checked', 2])
|
||||
assert_equal __, pattern_with_dropping(['I will not be checked', 'But I will!'])
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# We can use logical *or* in patterns
|
||||
|
||||
def alternative_pattern(variable)
|
||||
case variable
|
||||
in 0 | false | nil
|
||||
:match
|
||||
else
|
||||
:no_match
|
||||
end
|
||||
end
|
||||
|
||||
def test_alternative_pattern
|
||||
assert_equal __, alternative_pattern(0)
|
||||
assert_equal __, alternative_pattern(false)
|
||||
assert_equal __, alternative_pattern(nil)
|
||||
assert_equal __, alternative_pattern(4)
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# As pattern binds the variable to the value if pattern matches
|
||||
# pat: pat => var
|
||||
|
||||
def as_pattern
|
||||
a = 'First I was afraid'
|
||||
|
||||
case 'I was petrified'
|
||||
in String => a
|
||||
a
|
||||
else
|
||||
:no_match
|
||||
end
|
||||
end
|
||||
|
||||
def test_as_pattern
|
||||
assert_equal __, as_pattern
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# Array pattern works with all objects that have #deconstruct method that returns Array
|
||||
# It is useful to cut needed parts from Array-ish objects
|
||||
|
||||
class Deconstructible
|
||||
def initialize(str)
|
||||
@data = str
|
||||
end
|
||||
|
||||
def deconstruct
|
||||
@data&.split('')
|
||||
end
|
||||
end
|
||||
|
||||
def array_pattern(deconstructible)
|
||||
case deconstructible
|
||||
in 'a', *res, 'd'
|
||||
res
|
||||
else
|
||||
:no_match
|
||||
end
|
||||
end
|
||||
|
||||
def test_array_pattern
|
||||
assert_equal __, array_pattern(Deconstructible.new('abcd'))
|
||||
assert_equal __, array_pattern(Deconstructible.new('123'))
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# Hash pattern is quite the same as Array pattern, but it expects #deconsturct_keys(keys) method
|
||||
# It works with symbol keys for now
|
||||
|
||||
class LetterAccountant
|
||||
def initialize(str)
|
||||
@data = str
|
||||
end
|
||||
|
||||
def deconstruct_keys(keys)
|
||||
# we will count number of occurrences of each key in our data
|
||||
keys.map { |key| [key, @data.count(key.to_s)] }.to_h
|
||||
end
|
||||
end
|
||||
|
||||
def hash_pattern(deconstructible_as_hash)
|
||||
case deconstructible_as_hash
|
||||
in {a: a, b: b}
|
||||
[a, b]
|
||||
else
|
||||
:no_match
|
||||
end
|
||||
end
|
||||
|
||||
def test_hash_pattern
|
||||
assert_equal __, hash_pattern(LetterAccountant.new('aaabbc'))
|
||||
assert_equal __, hash_pattern(LetterAccountant.new('xyz'))
|
||||
end
|
||||
|
||||
# we can write it even shorter
|
||||
def hash_pattern_with_sugar(deconstructible_as_hash)
|
||||
case deconstructible_as_hash
|
||||
in a:, b:
|
||||
[a, b]
|
||||
else
|
||||
:no_match
|
||||
end
|
||||
end
|
||||
|
||||
def test_hash_pattern_with_sugar
|
||||
assert_equal __, hash_pattern_with_sugar(LetterAccountant.new('aaabbc'))
|
||||
assert_equal __, hash_pattern_with_sugar(LetterAccountant.new('xyz'))
|
||||
end
|
||||
|
||||
end
|
||||
@@ -38,4 +38,7 @@ require 'about_to_str'
|
||||
in_ruby_version("jruby") do
|
||||
require 'about_java_interop'
|
||||
end
|
||||
in_ruby_version("2.7") do
|
||||
require 'about_pattern_matching'
|
||||
end
|
||||
require 'about_extra_credit'
|
||||
|
||||
Reference in New Issue
Block a user