2016-12-25 3 views
1

Я уже задал соответствующий вопрос here, и ответ действительно исправить мою проблему, но у меня есть более общее недоразумение, как кристалл проверяет типы, так как я продолжаю сталкиваться с подобной проблемой, поэтому, если кто-то поможет мне понять это, я Будь великим. Я пробовал много вещей, которые отлично поработали с Ruby, но абсолютно не работают с Crystal (я знаю, что у них много различий, но я больше знаком с Ruby).Проверка типов в Crystal

Вот класс:

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} 

Который является именно то, что первым, если утверждение следует избегать. Поскольку я инициализирую 1D-массив, он не должен проходить через часть .size.

Я почти уверен, что это тривиальное объяснение, и что есть что-то довольно основное, что я не получил, но я хотел бы получить его, а не спотыкаться об этой проблеме все время.

Как я могу проверить тип в Crystal, так как способ, которым я пользуюсь, не так? Я пробовал использовать is_a?, == Type, используя метод is_matrix? (который отлично работает и выполняет определение, является ли он 2D или 1D, но все еще проходит через неправильную часть).

EDIT: в соответствии с первым ответом, я изменил 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 type, используя переменную экземпляра, которая является общим T. Компилятор не такой умный, и во время выполнения есть возможность, что @type изменяет и больше не отражает тип @values. Даже если вы этого не знаете, Crystal хочет быть в безопасности (иначе программа segfaults). Кристалл жалуется, потому что вы принимаете тип, который он не признает.

Капля @type. Это не помогает и усложняет ситуацию, чем нужно. Вы уже используете общий тип, теперь вы должны задать @values, что и есть, а затем действовать соответствующим образом. Кристалу это понравится, потому что вы убедитесь, что тип будет этим и ничем другим.

Это, как говорится, возможно, у вас должно быть 2 разных типа для обозначения Array против Array(Array). Может быть, с вашим кодом будет легче справиться?

+0

Спасибо за ваш ответ. Я пробовал то, что вы посоветовали, который напрямую оценивает тип @values, но не работает (см. Мое редактирование, я добавил то, что я пробовал).Когда вы говорите, что у вас есть 2 разных типа, вы имеете в виду наличие 2 разных классов? В идеале я пытаюсь создать тип, который будет иметь те же возможности, что и 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