Вы можете думать о первом аргументе блока как об аккумуляторе: результат каждого запуска блока сохраняется в аккумуляторе, а затем передается следующему выполнению блока. В случае кода, показанного выше, вы устанавливаете аккумулятор result по умолчанию на 0. Каждый запуск блока добавляет данное число к текущему итоговому значению, а затем сохраняет результат обратно в аккумулятор. Следующий вызов блока имеет это новое значение, добавляет к нему, сохраняет его снова и повторяется.
В конце процесса inject возвращает аккумулятор, который в данном случае представляет собой сумму всех значений в массиве, или 10.
Вот еще один простой пример создания хэша из массива объектов, привязанных к их строковому представлению:
[1,"a",Object.new,:hi].inject({}) do|hash, item|
hash[item.to_s] = item
hash
end
В этом случае мы используем по умолчанию для нашего аккумулятора пустой хеш, а затем заполняем его каждый раз при выполнении блока. Обратите внимание, что мы должны вернуть хэш в качестве последней строки блока, потому что результат блока будет сохранен обратно в аккумулятор.
отличное объяснение, однако, в примере, приведенном OP, что возвращается (например, хеш в вашем примере). Он заканчивается результатом + объяснение и должен иметь возвращаемое значение, да?
Projjol
1
@Projjol - result + explanationэто и преобразование в аккумулятор, и возвращаемое значение. Это последняя строка в блоке, которая делает неявный возврат.
KA01
88
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) + 410
Спасибо, что выписали шаги. Это очень помогло. Хотя меня немного смутило, имеете ли вы в виду, что на диаграмме ниже показано, как ниже реализован метод inject с точки зрения того, что передается в качестве аргументов для инъекции.
2
Схема ниже основана на том, как это можно реализовать; это не обязательно реализуется именно так. Вот почему я сказал, что это воображаемый набор шагов; он демонстрирует базовую структуру, но не точную реализацию.
[1, 2, 3, 4].inject(0) { |result, element| result + element }
что дает 10 на выходе.
Итак, перед тем как начать, давайте посмотрим, какие значения хранятся в каждой переменной:
result = 0 Ноль получен из inject (value), который равен 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| }
Здесь значения, выделенные жирным курсивом, представляют собой элементы, полученные из массива, а значения, выделенные жирным шрифтом, являются результирующими значениями.
Я надеюсь, что вы понимаете принцип работы #injectметода #ruby.
Что они сказали, но обратите внимание, что вам не всегда нужно указывать «начальное значение»:
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
такой же как
[1, 2, 3, 4].inject { |result, element| result + element } # => 10
Попробуй, подожду.
Если для inject не передается аргумент, первые два элемента передаются в первую итерацию. В приведенном выше примере результат равен 1, а element - 2 в первый раз, поэтому на один вызов блока меньше.
Число, которое вы помещаете внутри вашего () of inject, представляет собой начальную точку, это может быть 0 или 1000. Внутри каналов у вас есть два заполнителя | x, y |. x = любое число, которое у вас было внутри .inject ('x'), а второй результат представляет каждую итерацию вашего объекта.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
к каждому элементу в массиве. Для следующего элемента («элемент») значение, возвращаемое из блока, - «результат». То, как вы его назвали (с параметром), «результат» начинается со значения этого параметра. Итак, эффект складывается из элементов.
tldr; 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
Проще говоря, вы просматриваете (повторяете) этот массив ([1,2,3,4] ). Вы будете перебирать этот массив 4 раза, потому что в нем 4 элемента (1, 2, 3 и 4). Метод inject имеет 1 аргумент (число 0), и вы добавите этот аргумент к 1-му элементу (0 + 1. Это равно 1). 1 сохраняется в «результат». Затем вы добавляете этот результат (который равен 1) к следующему элементу (1 + 2. Это 3). Это будет сохранено в качестве результата. Продолжайте: 3 + 3 равно 6. И, наконец, 6 + 4 равно 10.
Этот код не допускает возможности не передать начальное значение, но может помочь объяснить, что происходит.
defincomplete_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
Это простое и довольно легкое для понимания объяснение:
Забудьте о «начальном значении», поскольку оно вначале несколько сбивает с толку.
> [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
и это похоже на вставку a +между 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.
Ответы:
Вы можете думать о первом аргументе блока как об аккумуляторе: результат каждого запуска блока сохраняется в аккумуляторе, а затем передается следующему выполнению блока. В случае кода, показанного выше, вы устанавливаете аккумулятор result по умолчанию на 0. Каждый запуск блока добавляет данное число к текущему итоговому значению, а затем сохраняет результат обратно в аккумулятор. Следующий вызов блока имеет это новое значение, добавляет к нему, сохраняет его снова и повторяется.
В конце процесса inject возвращает аккумулятор, который в данном случае представляет собой сумму всех значений в массиве, или 10.
Вот еще один простой пример создания хэша из массива объектов, привязанных к их строковому представлению:
[1,"a",Object.new,:hi].inject({}) do |hash, item| hash[item.to_s] = item hash end
В этом случае мы используем по умолчанию для нашего аккумулятора пустой хеш, а затем заполняем его каждый раз при выполнении блока. Обратите внимание, что мы должны вернуть хэш в качестве последней строки блока, потому что результат блока будет сохранен обратно в аккумулятор.
источник
result + explanation
это и преобразование в аккумулятор, и возвращаемое значение. Это последняя строка в блоке, которая делает неявный возврат.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 Ноль получен из inject (value), который равен 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| }
Здесь значения, выделенные жирным курсивом, представляют собой элементы, полученные из массива, а значения, выделенные жирным шрифтом, являются результирующими значениями.
Я надеюсь, что вы понимаете принцип работы
#inject
метода#ruby
.источник
Код выполняет итерацию по четырем элементам в массиве и добавляет предыдущий результат к текущему элементу:
источник
Что они сказали, но обратите внимание, что вам не всегда нужно указывать «начальное значение»:
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
такой же как
[1, 2, 3, 4].inject { |result, element| result + element } # => 10
Попробуй, подожду.
Если для inject не передается аргумент, первые два элемента передаются в первую итерацию. В приведенном выше примере результат равен 1, а element - 2 в первый раз, поэтому на один вызов блока меньше.
источник
Число, которое вы помещаете внутри вашего () of inject, представляет собой начальную точку, это может быть 0 или 1000. Внутри каналов у вас есть два заполнителя | x, y |. x = любое число, которое у вас было внутри .inject ('x'), а второй результат представляет каждую итерацию вашего объекта.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
источник
Inject применяет блок
к каждому элементу в массиве. Для следующего элемента («элемент») значение, возвращаемое из блока, - «результат». То, как вы его назвали (с параметром), «результат» начинается со значения этого параметра. Итак, эффект складывается из элементов.
источник
tldr;
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). Метод inject имеет 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
Вас смущает блокировка или почему у вас есть ценность в методе? Но хороший вопрос. Какой там метод оператора?
С чего это начинается?
#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 }
в блоке инспектора элементов.
источник
Это простое и довольно легкое для понимания объяснение:
Забудьте о «начальном значении», поскольку оно вначале несколько сбивает с толку.
> [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
и это похоже на вставку a
+
между 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
.источник
Существует еще одна форма метода .inject (), которая очень полезна [4,5] .inject (&: +), которая суммирует все элементы области
источник
Это просто
reduce
илиfold
, если вы знакомы с другими языками.источник
То же самое:
[1,2,3,4].inject(:+) => 10
источник