[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Я смотрю на этот код, но мой мозг не понимает, как число 10 может стать результатом. Не мог бы кто-нибудь объяснить, что здесь происходит?
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Я смотрю на этот код, но мой мозг не понимает, как число 10 может стать результатом. Не мог бы кто-нибудь объяснить, что здесь происходит?
Ответы:
Вы можете думать о первом аргументе блока как об аккумуляторе: результат каждого запуска блока сохраняется в аккумуляторе, а затем передается следующему выполнению блока. В случае кода, показанного выше, вы устанавливаете аккумулятор 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
1 + 5 = 6 2 + 6 = 8 3 + 8 = 11 11 + 4 = 15
Inject применяет блок
result + element
к каждому элементу в массиве. Для следующего элемента («элемент») значение, возвращаемое из блока, - «результат». То, как вы его назвали (с параметром), «результат» начинается со значения этого параметра. Итак, эффект складывается из элементов.
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
Вас смущает блокировка или почему у вас есть ценность в методе? Но хороший вопрос. Какой там метод оператора?
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 }
в блоке инспектора элементов.
Это простое и довольно легкое для понимания объяснение:
Забудьте о «начальном значении», поскольку оно вначале несколько сбивает с толку.
> [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 (&: +), которая суммирует все элементы области