在Crystal中键入check

我已经问了一个相关的问题here并且答案确实解决了我的问题,但我对水晶检查的类型有一个更普遍的误解,因为我一直遇到类似的问题,所以如果有人可以帮我解决这个问题,我已经尝试了许多事情 . 与Ruby合作很好,但绝对不使用Crystal(我知道他们有很多不同,但我对Ruby更熟悉) .

这是一堂课:

class Narray(T)
  getter shape
  getter values

  @shape : Tuple(Int32, Int32) | Nil

  def initialize(values : T)
    @values = values
    @type = T
    @shape = set_shape
  end

  def set_shape
    if (@type == Array(Array(Int32))) || (@type == Array(Array(Float64)))

      return {values.size, values[0].size}      # Line causing the error

    elsif (@type == Array(Int32)) || (@type == Array(Float64))
      return {1, @values.size}
    end
  end

  def is_matrix?
    if @values[0].is_a?(Array(Int32)) || @values[0].is_a?(Array(Float64))
      return true
    else
      return false
    end
  end
end

要定义一个数组,我应该这样做:

arr1 = Narray.new([1,2,3])

这将通过 set_shape 来检索数组的形状(表示行数和列数的元组) . 在这个方法中,我检查if语句是否是2D数组 . 但是,当运行上面的行时,我收到此错误:

in script.cr:16: undefined method 'size' for Int32

      return {values.size, values[0].size}

这正是第一个if语句应该避免的 . 因为我'm initializing a 1D array, it shouldn'经过 .size 部分 .

我很确定这是一个微不足道的解释,并且有一些非常基本的东西,我没有得到,但我想得到它而不是一直绊倒这个问题 .

我怎样才能检查Crystal中的类型,因为我的方式是正确的?我已经尝试使用 is_a?== Type ,使用 is_matrix? 方法(它工作正常,可以确定它是2D还是1D,但仍然运行错误的部分) .

编辑:根据第一个答案,我已经改变了 set_shape 方法:

def set_shape
    if (@values.is_a? Array(Array(Int32))) || (@values.is_a? Array(Array(Float64)))
      return {values.size, values[0].size}
    elsif (@values.is_a? Array(Int32)) || (@values.is_a? Array(Float64))
      return {1, @values.size}
    end
  end

但我仍然有完全相同的错误

回答(2)

3 years ago

您遇到的问题是您使用恰好是通用 T 的实例变量间接测试 @values 类型 . 编译器并不那么聪明,并且在运行时可能会发生 @type 更改并且不再反映 @values 类型 . 即使你知道它赢了't, Crystal wants to be safe (otherwise the program segfaults). Crystal complains because you assume a type that it won't承认 .

删掉 @type . 它没有帮助,使事情变得更加复杂 . 你已经使用了泛型,现在你应该问 @values 它是什么,然后采取相应的行动 . 水晶会喜欢这样,因为你确定类型将是这个而不是别的 .

话虽这么说,也许你应该有2种不同的类型来表示 Array vs Array(Array) . 也许你的代码会更容易处理?

3 years ago

以下代码有效:

class Narray(T)
  getter shape
  getter values

  @shape : Tuple(Int32, Int32) | Nil

  def initialize(@values : T)
    @shape = set_shape
  end

  def set_shape
    values = @values # => asign to local var
    if values.is_a?(Array(Array(Int32))) || values.is_a?(Array(Array(Float64)))
      return {values.size, values[0].size}
    elsif (T == Array(Int32)) || (T == Array(Float64))
      return {1, values.size}
    end
  end

  def is_matrix?
    if @values[0].is_a?(Array(Int32)) || @values[0].is_a?(Array(Float64))
      return true
    else
      return false
    end
  end
end

arr1 = Narray.new([[1, 2], [3, 4]])
pp arr1.set_shape # => {2,2}
pp arr1.is_matrix? # => true