Нужно простое объяснение метода ввода - PullRequest
128 голосов
/ 02 апреля 2009
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

Я смотрю на этот код, но мой мозг не замечает, как число 10 может стать результатом. Кто-нибудь может объяснить, что здесь происходит?

Ответы [ 15 ]

189 голосов
/ 02 апреля 2009

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

В конце процесса inject возвращает аккумулятор, который в данном случае является суммой всех значений в массиве, или 10.

Вот еще один простой пример создания хеша из массива объектов, основанный на их строковом представлении:

[1,"a",Object.new,:hi].inject({}) do |hash, item|
  hash[item.to_s] = item
  hash
end

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

80 голосов
/ 02 апреля 2009

inject принимает значение для начала (0 в вашем примере) и блок, и он запускает этот блок один раз для каждого элемента списка.

  1. На первой итерации оно передает указанное вами значение в качестве начального значения и первый элемент списка и сохраняет значение, возвращенное вашим блоком (в данном случае result + element).
  2. Затем он снова запускает блок, передавая результат первой итерации в качестве первого аргумента, а второй элемент списка в качестве второго аргумента, снова сохраняя результат.
  3. Так продолжается до тех пор, пока не будут использованы все элементы списка.

Самый простой способ объяснить это может быть показать, как работает каждый шаг, для вашего примера; это воображаемый набор шагов, показывающий, как этот результат может быть оценен:

[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
25 голосов
/ 13 июля 2016

Синтаксис для метода ввода следующий:

inject (value_initial) { |result_memo, object| block }

Давайте решим приведенный выше пример, т.е.

[1, 2, 3, 4].inject(0) { |result, element| result + element }

, который дает 10 в качестве вывода.

Итак, перед тем как начать, давайте посмотрим, какие значения хранятся в каждой переменной:

результат = 0 Ноль пришел от inject (значение), которое составляет 0

element = 1 Это первый элемент массива.

Окей !!! Итак, давайте начнем понимать приведенный выше пример

Шаг: 1 [<b><i>1</i></b>, 2, 3, 4].inject(<b>0</b>) { |<b>0</b>, <i><b>1</b></i>| <b>0</b> + <i><b>1</b></i> }

Шаг: 2 [1, <b><i>2</i></b>, 3, 4].inject(0) { |<b>1</b>, <b><i>2</i></b>| <b>1</b> + <b><i>2</i></b> }

Шаг: 3 [1, 2, <b><i>3</i></b>, 4].inject(0) { |<b>3</b>, <b><i>3</i></b>| <b>3</b> + <b><i>3</i></b> }

Шаг: 4 [1, 2, 3, <b><i>4</i></b>].inject(0) { |<b>6</b>, <b><i>4</i></b>| <b>6</b> + <b><i>4</i></b> }

Шаг: 5 [1, 2, 3, 4].inject(0) { |<b>10</b>, <b><i>Now no elements left in the array, so it'll return 10 from this step</i></b>| }

Здесь Полужирный курсив значения - это элементы, извлекаемые из массива, а просто значения Полужирный - результирующие значения.

Надеюсь, вы понимаете, как работает #inject метод #ruby.

18 голосов
/ 02 апреля 2009

Код перебирает четыре элемента в массиве и добавляет предыдущий результат к текущему элементу:

  • 1 + 2 = 3
  • 3 + 3 = 6
  • 6 + 4 = 10
14 голосов
/ 09 апреля 2014

Число, которое вы вводите внутри () инъекции, представляет собой начальное место, оно может быть 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

14 голосов
/ 02 апреля 2009

То, что они сказали, но учтите также, что вам не всегда нужно указывать «начальное значение»:

[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

совпадает с

[1, 2, 3, 4].inject { |result, element| result + element } # => 10

Попробуй, я подожду.

Когда аргумент для передачи не передается, первые два элемента передаются в первую итерацию. В приведенном выше примере результат равен 1, а element равен 2 в первый раз, поэтому в блоке делается на один вызов меньше.

6 голосов
/ 21 марта 2015

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 для работа с.

6 голосов
/ 02 апреля 2009

Внедрить применяется блок

result + element

для каждого элемента в массиве. Для следующего элемента («элемента») значение, возвращаемое из блока, является «результатом». Как вы назвали это (с параметром), «результат» начинается со значения этого параметра. Таким образом, эффект добавляет элементы вверх.

4 голосов
/ 09 января 2015
[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
3 голосов
/ 02 марта 2016

[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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...