2016-12-25 29 views
1

检查我已经问一个相关的问题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?== 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 

但我还是有相同的错误

回答

3

你的问题是,你间接测试@values键入,使用恰巧是通用的实例变量T。编译器不是那么聪明,并且在运行时有可能会更改@type并且不再反映@values类型。即使你知道它不会,水晶想要安全(否则程序段错误)。水晶抱怨,因为你承担一个它不会承认的类型。

删除@type。这不是帮助,而是让事情比他们必须更复杂。你已经使用了一个通用的,现在你应该问@values它是什么,然后采取相应的行动。水晶会喜欢这样,因为你确定这种类型将是这个,而不是别的。

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

+0

谢谢您的回答。我已经尝试了你的建议,即直接评估@values的类型,但没有起作用(请参阅我的编辑,我已经添加了我尝试过的)。当你说有两种不同的类型时,你的意思是有两个不同的类?理想情况下,我试图创建一个类型与numpy ndarrays相同的功能,非常方便。在我看来,为什么它很方便的一部分是因为它是1D/2D/Int/Float的一种类型。但我总是对建议开放,如果你对这个类有任何“设计”建议,它会很棒 –

+0

问题在于你一直提到'@ values'并且不提前制作它的本地副本'values = @ values'。该类型将被正确限制为一个局部变量,因为它的类型不会改变(它是本地的函数),但实例变量可能随时被另一个线程或另一个函数改变(它是一个共享值),所以它的类型类型不会受到'is_a?'调用的限制,并保持为联合。 –

1

下面的代码工作:

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