【问题标题】:Make Class member attribute of type Array(T) accept 2D arrays of T使 Array(T) 类型的 Class 成员属性接受 T 的二维数组
【发布时间】:2016-12-22 21:02:19
【问题描述】:

我已经定义了一个Container 类。在@values 属性中,我需要存储一个数组或二维数组,这些数组中的元素可以是 Int32 或 Float64。如果我这样初始化它:

class Container

  def initialize(value) 
    @values = values
  end

end

我收到一个错误:@values : Type, is inferred from assignments to it across the whole program.

如果我这样定义:

class Container

  def initialize(value : Array) 
    @values = values
  end

end

我得到:can't use Array(T) as the type of instance variable @values of Container(T), use a more specific type

我怎样才能让这个类更灵活,这样我才能做到:

Container.new([1,2,3])
Container.new([1.0, 3.0, 4.0])
Container.new([[1, 2], [4,3,2],[1]])
Container.new([[1.0, 4.5], [2.2, 0.0]])

【问题讨论】:

  • Container 的用例是什么?你能用它做什么?
  • 目标是能够将其用作 numpy ndarray,如果它是方形的,则能够对其应用一些矩阵运算,如果它们的形状允许,则可以在 ndarray 之间进行运算,并使用每个元素的类型和数组的形状。问题在于元素的类型。正如我上面写的,我找不到如何使它适用于 1D 和 2D 数组,以及 32Int 和 64Float

标签: crystal-lang


【解决方案1】:

经过一番挖掘,似乎确实有一个official way of doing this。但是,它必须计划好,因为在构造函数中使用该语法会给我从 Crystal 0.20.1 开始的以下内容

def initialize(value : Array(Array | Int32 | Float64))
    @values = value
end

Error in line 3: can't use Array(T) in unions yet, use a more specific type

如果我从您的示例数据中正确理解,似乎类型将是同质的(即数组将始终包含一种特定类型)。如果是这种情况,您可以简单地重载构造函数。这不是一个很好的解决方案,但也许它可以束缚你。

class Container

  def initialize(value : Array(Array)) 
    @values = value
    calculate
  end

  def initialize(value : Array(Int32))
    @values = value
    calculate
  end

  def initialize(value : Array(Array(Int32)))
    @values = value
    calculate
  end

  def initialize(value : Array(Array(Float64)))
    @values = value
    calculate
  end

  def initialize(value : Array(Float64))
    @values = value
    calculate
  end

  def calculate
    # do stuff here
  end

end

Container.new([1,2,3])
Container.new([1.0, 3.0, 4.0])
Container.new([[1, 2], [4,3,2],[1]])
Container.new([[1.0, 4.5], [2.2, 0.0]])

编辑:

由于@Sija 的评论,您似乎可以在不指定类型的情况下使用@faaq 的解决方案。他们还分享了@98​​7654322@,我认为这比重载构造函数要干净得多。

【讨论】:

  • 你理解正确!没有想到重载构造函数。好主意非常感谢!所以你是说第一种方法应该在未来的版本中工作?这是一种更简洁/优雅的方式。
  • 相信会的,但我不完全确定。 Crystal 的 gitter 是一个获得答案的好地方,我也会在有机会时询问社区。​​span>
【解决方案2】:

为什么不使用泛型?

class Container(Type)

  def initialize(@values : Type) 
    pp @values
    pp typeof(@values)
  end

end

value = [1,2,3]
Container(typeof(value)).new(value)
value = [1.0, 3.0, 4.0]
Container(typeof(value)).new(value)
value = [[1, 2], [4,3,2],[1]]
Container(typeof(value)).new(value)
value = [[1.0, 4.5], [2.2, 0.0]]
Container(typeof(value)).new(value)

输出是:

@values # => [1, 2, 3]
typeof(@values) # => Array(Int32)
@values # => [1.0, 3.0, 4.0]
typeof(@values) # => Array(Float64)
@values # => [[1, 2], [4, 3, 2], [1]]
typeof(@values) # => Array(Array(Int32))
@values # => [[1.0, 4.5], [2.2, 0.0]]
typeof(@values) # => Array(Array(Float64))

【讨论】:

  • 确实可以。但是,它使使用这个类变得很痛苦。写Container(typeof([[1.0, 4.5], [2.2, 0.0]])).new([[1.0, 4.5], [2.2, 0.0]]) 而不是Container.new([[1.0, 4.5], [2.2, 0.0]])。使用上面的解决方案,我可以轻松地声明实例并同时获取类型。但我同意,这在课程本身中更短,但我现在对可用性更感兴趣。非常感谢您提供解决方案
  • 您不需要显式传递 Type 参数,Container.new(value) 也可以。
猜你喜欢
  • 1970-01-01
  • 2012-12-05
  • 1970-01-01
  • 1970-01-01
  • 2018-10-23
  • 2019-12-27
  • 1970-01-01
  • 2018-01-01
  • 1970-01-01
相关资源
最近更新 更多