[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
У цьому випадку ми дефолтуємо наш акумулятор до порожнього хешу, а потім заповнюємо його щоразу, коли блок виконує. Зауважте, що ми повинні повернути хеш як останній рядок блоку, оскільки результат блоку буде зберігатися назад у акумуляторі.
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 Нуль прийшов від введення (значення), яке дорівнює 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 - це елементи, отримані з масиву, а значення Bold - Bold .
Я сподіваюся, що ви розумієте, як працює #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
до кожного елемента в масиві. Для наступного елемента ("елемента") значення, повернене з блоку, є "результат". Так, як ви його назвали (з параметром), "результат" починається зі значення цього параметра. Таким чином, ефект додає елементи.
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). Метод введення має 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
і це як, введіть +
між 1,2,3,4, зробивши його 1 + 2 + 3 + 4, і це 10. Це :+
спосіб вказати Рубі +
у вигляді символу.
Це досить легко зрозуміти та зрозуміти. І якщо ви хочете проаналізувати, як це працює поетапно, це так: виберіть 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 (&: +), яка додасть усі елементи області
Це те саме, що і це:
[1,2,3,4].inject(:+)
=> 10