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 Réponses :
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 }
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é.
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 }
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----------|
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).
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
combinaisonempê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 queString.x {...} .y {...}sont simplement deux méthodes, appeléesxety, qui acceptent chacune leur propre bloc . Ce formulaire ne fonctionne que lorsquex {...}renvoie quelque chose qui répond ày. Vous pouvez réécrire votre exemple sans le chaînage de méthode commearr1 = arr.product (arr); arr2 = arr1.reject {...}; arr3 = arr2.any? {...}.