【问题标题】:Type checking in CrystalCrystal 中的类型检查
【发布时间】:2016-12-25 18:25:38
【问题描述】:

我已经问过一个相关问题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 语句应该避免的。由于我正在初始化一个一维数组,它不应该通过.size 部分。

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

由于我使用的方式不正确,如何检查 Crystal 中的类型? 我尝试过使用is_a?== Typeis_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

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

【问题讨论】:

    标签: crystal-lang


    【解决方案1】:

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

    删除@type。这无济于事,并且使事情变得比他们必须做的更复杂。您已经使用了泛型,现在您应该询问@values 它是什么,然后采取相应的行动。 Crystal 会喜欢这样,因为您确定该类型将是 this 而不是别的。

    话虽如此,也许你应该有两种不同的类型来表示ArrayArray(Array)。也许您的代码会更容易处理?

    【讨论】:

    • 感谢您的回答。我已经尝试了您的建议,即直接评估 @values' 类型但没有用(请参阅我的编辑,我已经添加了我尝试过的内容)。当你说有 2 种不同的类型时,你的意思是有 2 个不同的类吗?理想情况下,我正在尝试创建一种与 numpy ndarrays 具有相同功能的类型,这非常方便。在我看来,它之所以方便的部分原因是因为它是 1D/2D/Int/Float 的一种类型。但我总是乐于接受建议,如果你对这门课有任何“设计”建议,那就太好了
    • 问题是你一直引用@values 并且没有事先用values = @values 制作它的本地副本。对于局部变量,类型将被正确限制,因为它的类型不会改变(它是函数的本地变量)但实例变量可能随时被另一个线程或另一个函数更改(它是一个共享值),所以它type 不受is_a? 调用的限制,保持为联合体。
    【解决方案2】:

    以下代码有效:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-29
      • 2011-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多