Как Ruby несколько блоков кода работают вместе / когда связаны? - PullRequest
1 голос
/ 09 мая 2019

Вот функция в Ruby, чтобы найти, если 2 уникальных числа в массиве складываются в сумму:

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))

Я запутался, как расчет работает для получения результата. Как вы можете видеть, я разбил функцию на несколько частей, чтобы визуализировать это. Я исследовал и понимаю методы .reject и .any? по отдельности.

Тем не менее, я все еще не понимаю, как все это сочетается в одном вкладыше. Как оцениваются 2 блока в комбинации? Я только нашел примеры с .reject с 1 блоком кода впоследствии. .reject применяется к обоим? Я также подумал, что между двумя блоками кода может быть неявное AND, но я попытался добавить 3-й фиктивный блок, но он не удался, поэтому на данный момент я просто не совсем уверен, как он работает вообще.

Ответы [ 3 ]

2 голосов
/ 10 мая 2019

Возвращаемый результат первого метода возвращается и используется вторым методом.

Это:

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

является функциональностью, эквивалентной:

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

Это может быть лучше всего визуализировано в pry (комментарии мои)

[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
2 голосов
/ 10 мая 2019

Каждая операция «соединяется» в следующую, которая визуализируется следующим образом:

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

Где часть A, вызывающая .product(arr), оценивает объект. Этот объект имеет метод reject, который вызывается впоследствии, а этот объект имеет метод any?, который вызывается по очереди. Это модная версия a.b.c.d, где один вызов используется для генерации объекта для последующего вызова.

Из этого не видно, что product возвращает Enumerator , который является объектом, который можно использовать для получения результатов, но сам по себе не является фактическим результатом. Это больше похоже на намерение вернуть результаты и возможность получать их множеством способов. Они могут быть объединены в цепочку для получения желаемого конечного продукта.

В качестве примечания этот код может быть уменьшен до:

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

Где метод repeated_permutation дает вам все двухзначные комбинации чисел без повторяющихся чисел. Это может быть легко увеличено до N цифр путем изменения этого параметра. include? проверяет наличие цели.

Если вы работаете с большими массивами, вы можете немного оптимизировать это:

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

Где это остановится на первом найденном совпадении и избежать дальнейших сумм. Вызов lazy приводит к распространению отдельных значений до конца цепочки, вместо того, чтобы каждая ступень цепочки работала до завершения, а затем перешла на следующую.

Идея lazy является одной из интересных вещей в Enumerable. Вы можете контролировать, как значения проходят через эти цепочки.

2 голосов
/ 09 мая 2019

Вы можете интерпретировать выражение с помощью следующих эквивалентных подстановок:

# 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 }

Каждый блок является аргументом для соответствующего соответствующего метода - один для reject и один для any?. Они оцениваются по порядку, и не объединены. Части, составляющие выражение, могут быть заключены в круглые скобки, чтобы показать это:

((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 }

Блоки в Ruby являются аргументами метода

Блоки в Ruby являются первоклассными синтаксическими структурами для передачи замыканий в качестве аргументов для методов. Если вы более знакомы с объектно-ориентированными концепциями, чем с функциональными, вот пример объекта (вида), выполняющего функцию замыкания:

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]

Что совпадает с:

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

То же самое, что и у самого идиоматика:

pairs.map { |a, b| a * b }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...