"неопределенный метод 'ноль' для 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) в таком случае.

http://play.crystal-lang.org/

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 с блоком:

http://play.crystal-lang.org/

z = (1..2).map do
  v = nil
  v = 1 if true
  v
end
puts z.sum { |x| x || 0 } #=> 2
Другие вопросы по тегам