Результат внедрения равен нулю - PullRequest
1 голос
/ 11 февраля 2012

Я пытаюсь накапливать некоторые значения, если они соответствуют условию.Почему этот фрагмент возвращает ноль, если я ожидал, что он вернет 2?

[[1, 2], [2, 3], [3, 8], [4, 2]].inject(0) { |s, e| s + e[1] if e[0] <= 1}

Разве inject не является правильным методом для этого?

Ответы [ 3 ]

6 голосов
/ 11 февраля 2012

Вы должны вернуть s;

[[1, 2], [2, 3], [3, 8], [4, 2]].inject(0) { |s, e| s += e[1] if e[0] <= 1; s}

Чище

[[1, 2], [2, 3], [3, 8], [4, 2]].inject(0){|s,(k,v)| s += (k<2 ? v : 0)}
3 голосов
/ 11 февраля 2012

Вы можете сделать это в несколько этапов:

>> a = [[1, 2], [2, 3], [3, 8], [4, 2]]
>> a.select { |e| e.first <= 1 }.inject(0) { |s, e| s += e.last }
=> 2
>> a.select { |e| e.first <= 1 }.map(&:last).inject(0, :+)
=> 2

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

Если вы не возражаете против эмуляции указателя с помощью хэша или массива, вы можете сделать это с помощью each_with_object:

>> a.each_with_object({ :sum => 0 }) { |(k,v), m| m[:sum] += v if k <= 1 }[:sum]
=> 2
>> a.each_with_object([0]) { |(k,v), m| m[0] += v if k <= 1 }.first
=> 2

Результат вашегоБлок inject используется как значение s при следующем вызове вашего блока.На втором итераторе это:

s + e[1] if e[0] <= 1

будет иметь значение nil, потому что e[0] будет 2. Последующие итерации также возвращают nil из вашего блока, потому что каждый e[0] больше, чем1 кроме первого.Вот почему вам нужно вернуть s из вашего блока.Если бы у вас был такой массив:

[[1, 2], [2, 3], [3, 8], [4, 2], [1, 11]]

, вы бы даже не получили nil из вашего inject, вы просто могли бы сделать исключение:

NoMethodError: undefined method `+' for nil:NilClass

когда ваш блок попытался добавить 11 к nil.

2 голосов
/ 11 февраля 2012

Еще один способ:

xs.map { |k, v| v if k <= 1 }.compact.inject(0, :+)

Обратите внимание, что Ruby немного страдает от недостатка понимания списков, и мы должны (несколько неэффективно) эмулировать его с помощью map + compact. На языке с LC это выглядело бы лучше: sum(v for (k, v) in xs if k <= 1).

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