2016-12-25 60 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