💎Ruby Tip💎 ¿Sabías que Ruby Soporta Coincidencia de Patrones?
Coincidencia de patrones es una funcionalidad que fue introducida en Ruby 2.7. Desde Ruby 3.0 en adelante, ya no es una funcionalidad experimental, y podemos empezar a usarla sin un molesto warning:
(irb):3: warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!
Pero, ¿Qué es Pattern Matching?
La coincidencia de patrones es una característica que permite comparar y entender la estructura de información organizada, como datos o variables. Esto se hace verificando cómo está organizada la información y asignando las partes coincidentes a variables locales para su uso posterior.
Pattern Matching es soportado mediante la sintaxis case / in. Importante no confundir con case / when ni tampoco mezclar. Si no existe match con ninguna expresión y tampoco hay un else definido, entonces se levanta una excepción del tipo NoMatchingPatternError.
case <expression>
in <pattern1>
# ...
in <pattern2>
# ...
else
# ...
end
Los patrones pueden ser:
-
Valor: Cualquier objeto de Ruby (se compara con el operador ===, como en ‘when’).
-
Array: Patrón de arreglo:
[<subpatrón>, <subpatrón>, <subpatrón>, ...]. -
Find: Patrón de búsqueda:
[*variable, <subpatrón>, <subpatrón>, <subpatrón>, ..., *variable]. -
Hash: Patrón de hash:
{clave: <subpatrón>, clave: <subpatrón>, ...}. -
Alternativa: Combinación de patrones con
|(barra vertical). -
Captura de variable:
<patrón> => variableovariable.
Pattern Matching en la práctica
Aquí tenemos una función que procesa datos:
# Define a method that uses pattern matching with case/in
def process_data(data)
case data
in { type: "number", value: Integer => num }
puts "Received a number: #{num}"
in { type: "string", value: String => str }
puts "Received a string: #{str}"
in { type: "array", value: Array => arr }
puts "Received an array: #{arr}"
in { type: "hash", value: Hash => hash }
puts "Received a hash: #{hash}"
else
puts "Received something else."
end
end
# Test the method with different data structures
process_data({ type: "number", value: 42 }) # Output: Received a number: 42
process_data({ type: "string", value: "Hello, Ruby!" }) # Output: Received a string: Hello, Ruby!
process_data({ type: "array", value: [1, 2, 3] }) # Output: Received an array: [1, 2, 3]
process_data({ type: "hash", value: { key: "value" } }) # Output: Received a hash: {:key=>"value"}
process_data({ type: "unknown", value: "unknown data" }) # Output: Received something else.
En este ejemplo, mostramos cómo realizar una búsqueda basada en un patrón de Hash. Destacamos una de las potentes funciones de Pattern Matching: la asignación de variables. Logramos asignar un valor del hash desestructurado a una variable, lo que nos permite trabajar con ese valor de manera posterior en nuestro código.
Deconstruct y Deconstruct_keys
Existen dos métodos especiales en coincidencia de patrones: deconstruct, llamado cuando se trata de evaluar sobre un Array, y deconstruct_keys, llamado cuando se trata de evaluar sobre un Hash. Veamos un ejemplo:
class Coordinate
attr_accessor :x, :y
def initialize(x, y)
@x = x
@y = y
end
def deconstruct
[@x, @y]
end
def deconstruct_key
{x: @x, y: @y}
end
end
En la clase Coordinate, se define un método deconstruct y deconstruct_key que retornan un Array y un Hash respectivamente.
Entonces, cuando una instancia de la clase Coordinate es evaluada sobre un array, lo que sucede es que el método deconstruct es llamado en la instancia a evaluar:
c = Coordinates.new(32,50)
case c
in [a,b]
p a #=> 32
p b #=> 50
end
Y cuando la misma instancia es evaluada sobre un Hash, entonces el método deconstruct_key es llamado:
case c
in {x:, y:}
p x #=> 32
p y #=> 50
end
Si te ha interesado el tema, te invito a buscar más información en la documentación. Existen otros elementos interesantes de la coincidencia de patrones, como el uso del operador pin (^) y Guard clauses (if y unless).
Hasta aquí con la pequeña introducción al tema. Si no conocías esta sintaxis, espero que te vayas con una nueva herramienta para seguir desarrollando tus proyectos con Ruby.
Happy Coding!