💎Ruby Tip💎 ¿Sabías que Ruby Soporta Coincidencia de Patrones?

Andrés

8 December 2023

5 mins

 💎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> => variable o variable.

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!

¡Hola a todos! 👋 ¿Disfrutaron leyendo el artículo? ¡Me encantaría conocer sus opiniones! 💬 No duden en dejar un comentario abajo, ya sea para compartir sus comentarios, preguntas o simplemente saludar. ¡No es necesario registrarse, solo compartan algo valioso! 😊
Hey there! 👋 Enjoyed reading the post? I'd love to hear your thoughts! 💬 Feel free to drop a comment below—whether it's feedback, questions, or just saying hi. No need to sign up, just share something valuable! 😊