开发者

Nil class when using Ruby injection

开发者 https://www.devze.com 2023-01-08 11:20 出处:网络
I\'m new to Ruby, and I\'m having a strange problem with the inject method. When I d开发者_如何学Goo:

I'm new to Ruby, and I'm having a strange problem with the inject method.

When I d开发者_如何学Goo:

(1..10).inject(0) {|count,x| count + 1}

the result is 10, as expected. But when I do

(1..10).inject(0) {|count,x| count + 1 if (x%2 == 0)}

I get an error:

NoMethodError: undefined method `+' for nil:NilClass
    from (irb):43
    from (irb):43:in `inject'
    from (irb):43:in `each'
    from (irb):43:in `inject'
    from (irb):43

I don't really understand why (presumably) count is nil in the second example, but not the first. In any case, how would I count evens from 1 to 10 using inject?


The expression count + 1 if (x%2 == 0) returns nil when the condition isn't true, which count gets set to because that's the nature of the inject method.

You could fix it by returning count + 1 when it's an even number and just count when it's not:

(1..10).inject(0) { |count,x| x % 2 == 0 ? count + 1 : count }

A completely different solution is to use select to select the even numbers and use the Array#length method to count them.

(1..10).select { |x| x % 2 == 0 }.length


As yjerem already pointed out, count + 1 if (x%2 == 0) will be evaluated to nil when x is odd. And , here is problem: the nil value will be assigned to count, so the next iteration will be nil + 1 ,which caused error reported.

It is important to understand how inject works(a copy from ruby-doc)

enum.inject(initial) {| memo, obj | block } => obj

enum.inject {| memo, obj | block } => obj

Combines the elements of enum by applying the block to an accumulator value (memo) and each element in turn. At each step, memo is set to the value returned by the block. The first form lets you supply an initial value for memo. The second form uses the first element of the collection as a the initial value (and skips that element while iterating).

A rule will keep you away from this kind of error : the block should always return the same type of value as that of the accumulator. If your example, the block will return a type of nil when x%2==0 if false.

(1..10).inject(0) {|count,x| count + 1 if (x%2 == 0)}

0

精彩评论

暂无评论...
验证码 换一张
取 消