1
votes

Comment Ruby plusieurs blocs de code fonctionnent-ils en conjonction / lorsqu'ils sont chaînés?

Voici une fonction dans Ruby pour trouver si 2 nombres uniques dans un tableau s'additionnent à une somme:

def sum_eq_n? (arr, n)
    return true if arr.empty? && n == 0

    p "first part array:" + String(arr.product(arr).reject { |a,b| a == b })
    puts "\n"

    p "first part bool:" + String(arr.product(arr).reject { |a,b| a == b }.any?)
    puts "\n"

    p "second part:" + String(arr.product(arr).reject { |a,b| a + b == n } )
    puts "\n"

    result = arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }
    return result
end

#define inputs
l1 = [1, 2, 3, 4, 5, 5]
n = 10

#run function
print "Result is: " + String(sum_eq_n?(l1, n))

Je ne comprends pas comment le calcul fonctionne pour produire un résultat. Comme vous pouvez le voir, j'ai décomposé la fonction en quelques parties pour visualiser cela. J'ai recherché et compris les méthodes .reject et .any? individuellement.

Cependant, je ne sais toujours pas comment cela convient à tous ensemble dans le 1 liner. Comment les 2 blocs sont-ils évalués en combinaison? Je n'ai trouvé que des exemples avec .reject avec 1 bloc de code par la suite. .reject est-il appliqué aux deux? J'ai également pensé qu'il pourrait y avoir un ET implicite entre les 2 blocs de code, mais j'ai essayé d'ajouter un troisième bloc factice et cela a échoué, donc à ce stade, je ne suis pas vraiment sûr de comment cela fonctionne.


3 commentaires

Ce n'est pas une réponse à votre question, mais il n'est pas très utile de cocher [1,5] et [5,1]. la méthode combinaison empêche cela. arr.uniq.combination (2) .any? {| combi | combi.sum == n}


Au lieu de String (...) , envisagez d'utiliser une interpolation, comme "first part of array: # {arr.product (...)}" ou le .join ('') méthode. Le code Ruby a tendance à préférer les appels de méthode au casting dur avec des éléments tels que String .


x {...} .y {...} sont simplement deux méthodes, appelées x et y , qui acceptent chacune leur propre bloc . Ce formulaire ne fonctionne que lorsque x {...} renvoie quelque chose qui répond à y . Vous pouvez réécrire votre exemple sans le chaînage de méthode comme arr1 = arr.product (arr); arr2 = arr1.reject {...}; arr3 = arr2.any? {...} .


3 Réponses :


2
votes

Vous pouvez interpréter l'expression via ces substitutions équivalentes:

pairs.map { |a, b| a * b }

Chaque bloc est un argument pour la méthode respective - un pour rejet code>, et un pour any? . Ils sont évalués dans l'ordre et ne sont pas combinés. Les parties qui composent l'expression peuvent être placées entre parenthèses pour montrer ceci:

multiply_pair = Proc.new { |a, b| a * b }
pairs.map(&multiply_pair)

Les blocs dans Ruby sont des arguments de méthode

Les blocs en Ruby sont des structures de syntaxe de première classe pour passer des fermetures en tant qu'arguments aux méthodes. Si vous êtes plus familier avec les concepts orientés objet que fonctionnels, voici un exemple d'objet (en quelque sorte) agissant comme une fermeture:

class MultiplyPairStrategy
  def perform(a, b)
    a * b
  end
end

def convert_using_strategy(pairs, strategy)
  new_array = []
  for pair in pairs do
    new_array << strategy.perform(*pair)
  end
  new_array
end

pairs = [
  [2, 3],
  [5, 4],
]

multiply_pair = MultiplyPairStrategy.new
convert_using_strategy(pairs, multiply_pair) # => [6, 20]

C'est la même chose comme:

((arr.product(arr)).reject { |a,b| a == b }).any? { |a,b| a + b == n }

# broken up lines:
(
  (
    arr.product(arr)          # pairs
  ).reject { |a,b| a == b }   # different_pairs
).any? { |a,b| a + b == n }

Ce qui est le même que le plus idiomatique:

# orig
arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }

# same as
pairs = arr.product(arr)
pairs.reject { |a,b| a == b }.any? { |a,b| a + b == n }

# same as
pairs = arr.product(arr)
different_pairs = pairs.reject { |a,b| a == b }
different_pairs.any? { |a,b| a + b == n }


3 commentaires

D'accord, les parenthèses ont beaucoup de sens .... donc le rejet est appliqué avec le bloc {| a, b | a == b}, et tout? est appliqué au bloc {| a, b | a + b == n}, correct? Je pense que ma confusion auparavant était que je pensais que le .any? méthode était appliquée au premier bloc. J'ai l'habitude de coder comme someInt.ToString (). Ici, j'essaye de convertir un int en une chaîne dans une autre langue. Mais dans ce cas, il semble que le .any? n'est pas appliqué à ce qui est avant (comme mon exemple), mais le bloc de code fourni après? (C'est ma première fois avec Ruby)


Vous devez comprendre une chose: les blocs dans Ruby sont des arguments de méthode. J'ai ajouté une section à ma réponse pour le démontrer.


D'accord merci beaucoup. Cela a beaucoup de sens. Cela m'a déjà beaucoup aidé.



2
votes

Le résultat de retour de la première méthode est retourné et utilisé par la deuxième méthode.

Ceci:

[1] pry(main)> arr = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
[2] pry(main)> n = 10
=> 10
[3] pry(main)> result_reject = arr.product(arr).reject { |a,b| a == b } # all combinations of array elements, with identical ones removed
=> [[1, 2],
 [1, 3],
 [1, 4],
 [1, 5],
 [1, 5],
 [2, 1],
 [2, 3],
 [2, 4],
 [2, 5],
 [2, 5],
 [3, 1],
 [3, 2],
 [3, 4],
 [3, 5],
 [3, 5],
 [4, 1],
 [4, 2],
 [4, 3],
 [4, 5],
 [4, 5],
 [5, 1],
 [5, 2],
 [5, 3],
 [5, 4],
 [5, 1],
 [5, 2],
 [5, 3],
 [5, 4]]
[4] pry(main)> result_reject.any? { |a,b| a + b == n } # do any of the pairs of elements add together to equal ` n` ?
=> false
[5] pry(main)> arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }  # the one liner
=> false

est une fonctionnalité équivalente à:

results = arr.product(arr).reject { |a,b| a == b} # matrix of array pairs with identical values rejected
result = results.any? { |a,b| a + b == n }  #true/false

Cela pourrait être mieux visualisé dans pry (commentaires miens)

result = arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }


0 commentaires

2
votes

Chaque opération "enchaîne" la suivante, qui visualisée ressemble à:

arr.repeated_permutation(2).lazy.map(&:sum).include?(n)

Où la partie A, appelant .product (arr) , s'évalue en un objet . Cet objet a une méthode rejet qui est appelée par la suite, et cet objet a une méthode any? qui est appelée à son tour. C'est une version sophistiquée de abcd où un appel est utilisé pour générer un objet pour un appel suivant.

Ce qui ne ressort pas de cela, c'est le fait que product code > renvoie un Enumerator , qui est un objet qui peut être utilisé pour récupérer le résultats, mais ne sont pas les résultats réels en soi. Cela ressemble plus à une intention de renvoyer les résultats et à une capacité à les récupérer de multiples façons. Ceux-ci peuvent être enchaînés pour obtenir le produit final souhaité.

Pour mémoire, ce code peut être réduit à:

arr.repeated_permutation(2).map(&:sum).include?(n)

Où le repeat_permutation vous donne tout Combinaisons de nombres à 2 chiffres sans numéros en double. Cela peut être facilement mis à l'échelle jusqu'à N chiffres en modifiant ce paramètre. include? teste si la cible est présente.

Si vous travaillez avec de grands tableaux, vous voudrez peut-être optimiser légèrement ceci:

arr.product(arr).reject { |a,b| a == b }.any? { |a,b| a + b == n }
|--|------A----->-----------B----------->-------------C----------|


1 commentaires

Puisque l'addition est commutative, la combinaison (2) (comme suggéré par @steenslag) est meilleure que repeat_permutation (2) . En outre, il peut être utile de noter que lazy optimise la mémoire au détriment de la vitesse (au moins dans tous les Ruby que j'ai testés jusqu'à présent).