"неопределенный метод 'ноль' для Nil:Class", когда #sum массив без Nils
Проблема возникает, когда переменная, из которой был построен массив, была nil
первоначально.
y = (1..2).map do
v = nil
v = 1
v
end
p y # => [1, 1]
p y.class # => Array(Int32)
p y.sum # => 2
когда v
перестает быть nil
в условии, которое потенциально вычислительно и не решаемо при компиляции:
z = (1..2).map do
v = nil
v = 1 if true
v
end
p z # [1, 1]
p z.class # => Array(Nil | Int32)
Массив получает более сложный тип, который не совместим с текущим sum
реализация, так p z.sum
вызывает ошибку времени компиляции:
undefined method 'zero' for Nil:Class (compile-time type is (Nil | Int32):Class)
def sum(initial = T.zero)
^~~~
Как я должен бороться с этим должным образом?
Или, может быть, он ждет лучшей реализации stdlib sum
метод или что-нибудь еще?
UPD: inject
дает то же самое:
p z.inject{ |i, j| i + j }
undefined method '+' for Nil (compile-time type is (Nil | Int32))
2 ответа
Ты можешь использовать Iterator#compact_map
выбрать ненулевые значения. Компилятор сможет вывести Array(Int32)
в таком случае.
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]
Также обратите внимание, что typeof(Expr)
а также Expr.class
может привести к другим результатам. Первый тип времени компиляции, а позже тип времени выполнения.
Альтернативное решение того, что говорит Брайан, состоит в использовании sum
с блоком:
z = (1..2).map do
v = nil
v = 1 if true
v
end
puts z.sum { |x| x || 0 } #=> 2