Ruby 3.2 introduit Enumerator::product
C’est à nouveau cette période de l’année où une nouvelle version de Ruby est publiée. Examinons de plus près une nouvelle méthode Enumerator : ::product.
Syntaxe de la Méthode
Voici le fil de discussion pour ce changement : https://bugs.ruby-lang.org/issues/18685
Pour comprendre ce que fait cette nouvelle méthode Enumerator::product, imaginez que vous voulez obtenir un produit cartésien de deux tableaux comme ceci :
array_1 = [1, 2, 3]
array_2 = [:a, :b, :c]
# Produit cartésien
[[1, :a], [1, :b], [1, :c], [2, :a], [2, :b], [2, :c], [3, :a], [3, :b], [3, :c]]
Pour pouvoir faire cela avant Ruby 3.2, il fallait imbriquer les tableaux. Voici une solution tirée de cette réponse StackOverflow :
class CartesianProduct
include Enumerable
def initialize(xs, ys)
@xs = xs
@ys = ys
end
def each
return to_enum unless block_given?
@xs.each do |x|
@ys.each { |y| yield [x, y] }
end
end
end
products = CartesianProduct.new(array_1, array_2).each.to_a
# [[1, :a], [1, :b], [1, :c], [2, :a], [2, :b], [2, :c], [3, :a], [3, :b], [3, :c]]
À partir de Ruby 3.2, nous pouvons faire ceci à la place :
products = Enumerator.product(array_1, array_2).to_a
# [[1, :a], [1, :b], [1, :c], [2, :a], [2, :b], [2, :c], [3, :a], [3, :b], [3, :c]]
Applications Pratiques
Voyons comment nous pourrions utiliser cette méthode. Imaginez que nous construisons une application e-commerce et que nous devons créer un tableau d’options pour un t-shirt.
Le t-shirt aurait les options suivantes :
- Taille (Petit, Moyen, Grand)
- Couleur (Bleu, Rouge, Vert, Jaune)
- Tissu (Coton, Nylon)
En utilisant Enumerator::product, nous pouvons rapidement construire un tableau d’options comme ceci :
colors = ['blue', 'red', 'green', 'yellow']
sizes = ['small', 'medium', 'large']
fabrics = ['cotton', 'nylon']
variants = Enumerator.product(colors, sizes, fabrics).to_a
# [
# ["blue", "small", "cotton"],
# ["blue", "small", "nylon"],
# ["blue", "medium", "cotton"],
# ["blue", "medium", "nylon"],
# ["blue", "large", "cotton"],
# ...
# ]
Génial ! Maintenant nous n’avons pas besoin d’itérer à travers toutes les différentes combinaisons pour obtenir les bonnes options.
Creusons Plus Profond
Certains lecteurs remarqueront peut-être aussi que les versions récentes de Ruby incluent déjà une méthode Array#product. Pour cette méthode, elle est définie ici : https://github.com/ruby/ruby/blob/v3_2_0_rc1/tool/transcode-tblgen.rb#L10-L20
def product(*args)
if args.empty?
self.map {|e| [e] }
else
result = []
self.each {|e0|
result.concat args.first.product(*args[1..-1]).map {|es| [e0, *es] }
}
result
end
end
Cependant, l’implémentation de la nouvelle méthode Enumerable::product est différente et implémentée en C. Ce code est trop long pour être collé ici, donc voici le lien : https://github.com/ruby/ruby/blob/v3_2_0_rc1/enumerator.c#L3382.