[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Я смотрю на этот код, но мой мозг не регистрирует, как число 10 может стать результатом. Кто-нибудь подумает, что здесь происходит?
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Я смотрю на этот код, но мой мозг не регистрирует, как число 10 может стать результатом. Кто-нибудь подумает, что здесь происходит?
Вы можете думать о первом блочном аргументе в качестве аккумулятора: результат каждого запуска блока хранится в накопителе, а затем передается следующему исполнению блока. В случае кода, показанного выше, вы по умолчанию используете аккумулятор, результат, равный 0. Каждый запуск блока добавляет заданный номер к текущему итогу, а затем сохраняет результат обратно в аккумулятор. Следующий вызов блока имеет это новое значение, добавляет к нему, сохраняет его снова и повторяет.
В конце процесса впрыск возвращает накопитель, который в этом случае является суммой всех значений в массиве или 10.
Вот еще один простой пример создания хэша из массива объектов, с помощью строкового представления:
[1,"a",Object.new,:hi].inject({}) do |hash, item|
hash[item.to_s] = item
hash
end
В этом случае мы по умолчанию используем наш аккумулятор для пустого хэша, а затем заполняем его каждый раз, когда выполняется блок. Обратите внимание, что мы должны вернуть хеш в качестве последней строки блока, потому что результат блока будет сохранен обратно в аккумуляторе.
inject
принимает значение для начала (0
в вашем примере) и блок, и он запускает этот блок один раз для каждого элемента списка.
result + element
).Самый простой способ объяснить это может состоять в том, чтобы показать, как каждый шаг работает для вашего примера; это мнимый набор шагов, показывающий, как этот результат можно оценить:
[1, 2, 3, 4].inject(0) { |result, element| result + element }
[2, 3, 4].inject(0 + 1) { |result, element| result + element }
[3, 4].inject((0 + 1) + 2) { |result, element| result + element }
[4].inject(((0 + 1) + 2) + 3) { |result, element| result + element }
[].inject((((0 + 1) + 2) + 3) + 4) { |result, element| result + element }
(((0 + 1) + 2) + 3) + 4
10
Синтаксис для метода инъекции выглядит следующим образом:
inject (value_initial) { |result_memo, object| block }
Пусть решается приведенный выше пример, т.е.
[1, 2, 3, 4].inject(0) { |result, element| result + element }
который дает 10 в качестве выхода.
Итак, перед запуском давайте посмотрим, какие значения хранятся в каждой переменной:
result = 0 Нуль пришел из инъекции (значения), которая равна 0
element = 1 Это первый элемент массива.
Окей!!! Итак, давайте начнем понимать приведенный выше пример
Шаг: 1 [1, 2, 3, 4].inject(0) { |0, 1| 0 + 1 }
Шаг: 2 [1, 2, 3, 4].inject(0) { |1, 2| 1 + 2 }
Шаг: 3 [1, 2, 3, 4].inject(0) { |3, 3| 3 + 3 }
Шаг: 4 [1, 2, 3, 4].inject(0) { |6, 4| 6 + 4 }
Шаг: 5 [1, 2, 3, 4].inject(0) { |10, Now no elements left in the array, so it'll return 10 from this step| }
Здесь значения Bold-Italic - это выборка элементов из массива, а просто значения Полужирный - это результирующие значения.
Я надеюсь, что вы поймете работу метода #inject
#ruby
.
Код выполняет итерацию по четырем элементам массива и добавляет предыдущий результат к текущему элементу:
Что они сказали, но обратите внимание также, что вам не всегда нужно указывать "начальное значение":
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
совпадает с
[1, 2, 3, 4].inject { |result, element| result + element } # => 10
Попробуй, я подожду.
Когда аргумент не передается для ввода, первые два элемента передаются в первую итерацию. В приведенном выше примере результат равен 1, а элемент равен 2 в первый раз, поэтому в блок делается меньше вызовов.
Число, которое вы помещаете внутри вашего() инъекции, представляет собой начальное место, оно может быть 0 или 1000. Внутри труб у вас есть два держателя | x, y |. x = то, что у вас было внутри объекта .inject('x'), а secound представляет каждую итерацию вашего объекта.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
1 + 5 = 6 2 + 6 = 8 3 + 8 = 11 11 + 4 = 15
В приложении применяется блок
result + element
для каждого элемента массива. Для следующего элемента ( "элемент" ) значение, возвращаемое из блока, является "результатом". То, как вы его назвали (с параметром), "результат" начинается со значения этого параметра. Таким образом, эффект заключается в добавлении элементов вверх.
TL;DR; inject
отличается от map
одним важным способом: inject
возвращает значение последнего выполнения блока, тогда как map
возвращает массив, который он повторил.
Более того значение каждого исполнения блока передается в следующее выполнение через первый параметр (result
в этом случае), и вы можете инициализировать это значение (часть (0)
).
Ваш приведенный выше пример можно записать с помощью map
следующим образом:
result = 0 # initialize result
[1, 2, 3, 4].map { |element| result += element }
# result => 10
Тот же эффект, но inject
более краткий.
Часто вы обнаружите, что назначение выполняется в блоке map
, тогда как оценка выполняется в блоке inject
.
Какой метод вы выбираете, зависит от области действия, которую вы хотите использовать для result
. Когда не использовать, это будет примерно так:
result = [1, 2, 3, 4].inject(0) { |x, element| x + element }
Вы можете быть похожими на всех: "Посмотри, я только что объединил все в одну строку", но вы также временно выделили память для x
как переменную нуля, которая не была необходимой, поскольку у вас уже было result
до работать с.
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
эквивалентно следующему:
def my_function(r, e)
r+e
end
a = [1, 2, 3, 4]
result = 0
a.each do |value|
result = my_function(result, value)
end
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
На простом английском языке вы проходите (итерацию) через этот массив ([1,2,3,4]
). Вы будете проходить через этот массив 4 раза, потому что есть 4 элемента (1, 2, 3 и 4). Инъекционный метод имеет 1 аргумент (число 0), и вы добавите этот аргумент в 1-й элемент (0 + 1. Это равно 1). 1 сохраняется в "результате". Затем вы добавляете этот результат (который равен 1) к следующему элементу (1 + 2. Это 3). В результате это будет сохранено. Продолжайте движение: 3 + 3 равно 6. И, наконец, 6 + 4 равно 10.
Этот код не позволяет не передавать начальное значение, но может помочь объяснить, что происходит.
def incomplete_inject(enumerable, result)
enumerable.each do |item|
result = yield(result, item)
end
result
end
incomplete_inject([1,2,3,4], 0) {|result, item| result + item} # => 10
Начните здесь, а затем просмотрите все методы, которые принимают блоки. http://ruby-doc.org/core-2.3.3/Enumerable.html#method-i-inject
Является ли это блоком, который вас смущает или почему у вас есть значение в методе? Хороший вопрос. Что такое оператор?
result.+
Что начинается с?
#inject(0)
Можем ли мы это сделать?
[1, 2, 3, 4].inject(0) { |result, element| result.+ element }
Это работает?
[1, 2, 3, 4].inject() { |result = 0, element| result.+ element }
Вы видите, что я основываюсь на идее, что она просто суммирует все элементы массива и дает число в заметке, которую вы видите в документах.
Вы всегда можете сделать это
[1, 2, 3, 4].each { |element| p element }
чтобы увидеть, как перечислимый массив проходит через. Это основная идея.
Это просто инъекция или сокращение дает вам записку или аккумулятор, который отправляется.
Мы могли бы попытаться получить результат
[1, 2, 3, 4].each { |result = 0, element| result + element }
но ничего не возвращается, так что это действует так же, как и раньше
[1, 2, 3, 4].each { |result = 0, element| p result + element }
в блоке инспектора элементов.
Существует другая форма метода .inject(). Это очень полезно. [4,5].inject(&: +) Это добавит весь элемент области
Это просто reduce
или fold
, если вы знакомы с другими языками.
Это простое и довольно простое для понимания объяснение:
Забудьте о "начальном значении", так как в начале оно несколько сбивает с толку.
> [1,2,3,4].inject{|a,b| a+b}
=> 10
Вы можете понять вышесказанное следующим образом: я ввожу "машину добавления" между 1,2,3,4. Это означает, что это 1 ♫ 2 ♫ 3 ♫ 4, а ♫ - это сложение, поэтому оно равно 1 + 2 + 3 + 4 и равно 10.
Вы можете вставить между ними +
:
> [1,2,3,4].inject(:+)
=> 10
и это похоже на вставку +
между 1,2,3,4, делая его 1 + 2 + 3 + 4 и равным 10. :+
- это Ruby-способ определения +
в форма символа.
Это довольно легко понять и интуитивно понятно. И если вы хотите проанализировать, как это работает, шаг за шагом, это как: взять 1 и 2, а затем добавить их, а когда у вас есть результат, сначала сохранить его (который равен 3), а теперь, следующий хранится значение 3 и элемент массива 3 проходят через процесс a + b, который равен 6, и теперь сохраняют это значение, а теперь 6 и 4 проходят процесс a + b и равен 10. По сути, вы выполняете
((1 + 2) + 3) + 4
и равно 10. "Начальное значение" 0
- это просто "основа" для начала. Во многих случаях вам это не нужно. Представьте, если вам нужно 1 * 2 * 3 * 4, и это
[1,2,3,4].inject(:*)
=> 24
и это сделано. Вам не нужно "начальное значение" 1
, чтобы умножить все это на 1
.
То же самое:
[1,2,3,4].inject(:+)
=> 10