2 votes

Meilleure façon d'entrelacer deux enums en ruby ?

Je cherche un moyen plus élégant de mélanger deux ensembles de résultats SQL avec un rapport donné. Dans chacun d'eux, je veux qu'ils soient traités dans le même ordre qu'ils arrivent, mais je veux entrelacer le traitement pour obtenir le mélange souhaité.

J'ai réalisé que cela pouvait être transformé en une méthode très générique fonctionnant avec deux enums et produisant des éléments à traiter. J'ai donc écrit cette méthode dont je suis à la fois assez fier (belle solution générique) et assez honteux.

def combine_enums_with_ratio(enum_a, enum_b, desired_ratio)
  a_count = 1
  b_count = 1
  a_finished = false
  b_finished = false
  loop do
    ratio_so_far = a_count / b_count.to_f
    if !a_finished && (b_finished || ratio_so_far <= desired_ratio)
      begin
        yield enum_a.next
        a_count += 1
      rescue StopIteration
        a_finished = true
      end
    end

    if !b_finished && (a_finished || ratio_so_far > desired_ratio)
      begin
        yield enum_b.next
        b_count += 1
      rescue StopIteration
        b_finished = true
      end
    end

    break if a_finished && b_finished
  end
end

Honteux parce que c'est clairement écrit dans un style très impératif. Ce n'est pas très ruby. Peut-être qu'il y a un moyen d'utiliser l'une des méthodes déclaratives de bouclage de Ruby, mais elles ne semblent pas fonctionner en tenant deux enums ouverts comme ceci. Alors je crois que je dois sauver une exception dans le cadre d'un flux de contrôle comme celui-ci, ce qui semble très sale. Il me manque la méthode de java hasNext() méthode.

Y a-t-il un meilleur moyen ?

J'ai trouvé une question similaire sur en comparant enums : Ruby - Comparer deux énumérateurs de manière élégante . Quelques réponses compactes, mais pas particulièrement de résolution, et mon problème impliquant des longueurs inégales et des rendements inégaux semble plus délicat.

3voto

Stefan Points 23363

Voici une approche plus courte et plus générale :

def combine_enums_with_ratio(ratios)
  return enum_for(__method__, ratios) unless block_given?

  counts = ratios.transform_values { |value| Rational(1, value) }

  until counts.empty?
    begin
      enum, _ = counts.min_by(&:last)
      yield enum.next
      counts[enum] += Rational(1, ratios[enum])
    rescue StopIteration
      counts.delete(enum)
    end
  end
end

Au lieu de deux enums, il prend un hash de enum => ratio paires.

Dans un premier temps, il crée un counts hachage en utilisant la réciproque du rapport, c'est-à-dire enum_a => 3, enum_b => 2 devient :

counts = { enum_a => 1/3r, enum_b => 1/2r }

Puis, à l'intérieur d'une boucle, il récupère la valeur minimale du hachage, soit enum_a dans l'exemple ci-dessus. Il donne son next et incrémente sa valeur counts valeur du ratio :

counts[enum_a] += 1/3r

counts #=> {:enum_a=>(2/3), :enum_b=>(1/2)}

A la prochaine itération, enum_b a la plus petite valeur, donc son next sera donnée et son rapport sera incrémenté :

counts[enum_b] += 1/2r

counts #=> {:enum_a=>(2/3), :enum_b=>(1/1)}

Si vous continuez à incrémenter enum_a par (1/3) y enum_b par (1/2) le rapport de rendement de leurs éléments sera de 3:2.

Enfin, le rescue permet de gérer les enums qui manquent d'éléments. Si cela se produit, l'énumération en question est retirée de l'en-tête. counts haché.

Une fois que le counts est vide, la boucle s'arrête.

Exemple d'utilisation avec 3 enums :

enum_a = (1..10).each
enum_b = ('a'..'f').each
enum_c = %i[foo bar baz].each

combine_enums_with_ratio(enum_a => 3, enum_b => 2, enum_c => 1).to_a
#=> [1, "a", 2, 3, "b", :foo, 4, "c", 5, 6, "d", :bar, 7, "e", 8, 9, "f", :baz, 10]
#    <--------------------->  <--------------------->  <--------------------->
#             3:2:1                    3:2:1                     3:2:1

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X