简体   繁体   中英

“undefined method 'zero' for Nil:Class” when #sum the Array without Nils

The issue happens when the variable, that the array was built from, was a nil initially.

y = (1..2).map do
  v = nil
  v = 1
  v
end
p y       # => [1, 1]
p y.class # => Array(Int32)
p y.sum   # => 2

When v stops being nil on a condition, that is potentially computational and not solvable while compiling:

z = (1..2).map do
  v = nil
  v = 1 if true
  v
end
p z       # [1, 1]
p z.class # => Array(Nil | Int32)

The array gets more complex type, that isn't compatible with current sum implementation, so p z.sum causes compile time error:

undefined method 'zero' for Nil:Class (compile-time type is (Nil | Int32):Class)
 def sum(initial = T.zero)
                     ^~~~

How am I supposed to fight this properly?
Or maybe it waits for some better implementation of stdlib sum method or anything else?

UPD: inject gives the same:

p z.inject{ |i, j| i + j }

undefined method '+' for Nil (compile-time type is (Nil | Int32))

You can use Iterator#compact_map to select non-nil values. The compiler will be able to infer a Array(Int32) in that case.

http://play.crystal-lang.org/#/r/e85

z = (1..2).map do
  v = nil
  v = 1 if true
  v
end

pp typeof(z) # => Array(Nil | Int32)
pp z # => z = [1, 1]

y = z.compact_map(&.itself)
pp typeof(y) # => Array(Int32)
pp y # => y = [1, 1]

Also, notice that typeof(Expr) and Expr.class might lead to different results. The first is the compile time type and the later is the runtime type.

An alternative solution to what Brian says is to use sum with a block:

http://play.crystal-lang.org/#/r/ein

z = (1..2).map do
  v = nil
  v = 1 if true
  v
end
puts z.sum { |x| x || 0 } #=> 2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM