输入检查 Crystal

Type checking in Crystal

我已经问了一个相关问题 ,答案确实解决了我的问题,但我对 crystal 如何检查类型有更普遍的误解,因为我一直 运行ning 类似问题,所以如果有人可以帮助我解决这个问题,我将不胜感激。我已经尝试了很多与 Ruby 一起工作但绝对不能与 Crystal 一起工作的东西(我知道它们有很多差异,但我更熟悉 Ruby)。

这是一个class:

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 语句是否为二维数组。 但是,当 运行ning 上面的行时,我得到这个错误:

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

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

这正是第一个 if 语句应该避免的。因为我正在初始化一维数组,所以它不应该经过 .size 部分。

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

我如何检查 Crystal 中的类型,因为我使用的方式不正确? 我试过使用 is_a?== Type,使用 is_matrix? 方法(效果很好,可以确定它是 2D 还是 1D,但仍然 运行s 通过错误的部分)。

编辑:根据第一个答案,我更改了 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

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

您遇到的问题是您使用恰好是通用 T 的实例变量间接测试 @values 类型。编译器不是那么聪明,并且有可能在运行时 @type 发生变化并且不再反映 @values 类型。即使您知道它不会,Crystal 也希望安全(否则程序会出现段错误)。 Crystal 抱怨是因为你假设了一个它不会承认的类型。

放弃 @type。它没有帮助,而且使事情变得比他们必须的更复杂。您已经使用了泛型,现在您应该询问 @values 它是什么,然后采取相应的行动。 Crystal 会喜欢的,因为你确定类型就是这个,没有别的。

也就是说,也许你应该有 2 种不同的类型来表示 ArrayArray(Array)。也许您的代码更容易处理?

以下代码有效:

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