web-dev-qa-db-ja.com

Rubyでハッシュの代わりにStructを使用するのはいつですか?

プログラミングの経験はあまりありません。しかし、私にとって、StructはHashにいくらか似ているようです。

  • Structがうまくできることは何ですか?
  • Structができること、Hashができないことはありますか?

グーグルの後、CではStructの概念が重要ですが、Cについてはあまり知りません。

70
TK.

構造体は、次の点で(コードの外観に加えて)ハッシュマップの使用とは異なります。

  • 構造体には固定の属性セットがあり、新しいキーをハッシュに追加します。
  • 構造体のインスタンスに存在しない属性を呼び出すと、NoMethodErrorが発生しますが、存在しないキーの値をハッシュから取得すると、nilが返されます。
  • 構造体の属性が同じでインスタンスの値が同じであっても、構造体の異なる2つのインスタンスが等しくなることはありません(つまり、Struct.new(:x).new(42) == Struct.new(:x).new(42)はfalse、Foo = Struct.new(:x); Foo.new(42)==Foo.new(42)はtrue)。
  • 構造体の_to_a_メソッドは値の配列を返しますが、ハッシュの_to_a_はキーと値のペアの配列を取得します(「ペア」は「2要素の配列」を意味します)
  • Foo = Struct.new(:x, :y, :z)の場合、Foo.new(1,2,3)を実行して、属性名を指定する必要なくFooのインスタンスを作成できます。

したがって、質問に答えるには、既知の一連の属性を持つオブジェクトをモデル化する場合は、構造体を使用します。任意の使用ハッシュマップをモデル化する場合(たとえば、各Wordが文字列内に出現する頻度をカウントしたり、ニックネームをフルネームにマッピングしたりすることなど)は、構造体の仕事ではありませんが、名前、年齢、住所を使用して人をモデル化すると、 Person = Struct.new(name, age, address))に最適です。

補足として:Cの構造体は、Rubyの構造体とはほとんど関係がないので、混乱しないようにしてください。

87
sepp2k

私はこの質問がほとんど答えられたことを知っていますが、驚くべきことに、Structの最大の違いの1つと実際の利点について誰も話していません。そして、それが理由だと思います 誰かがまだ質問しています

私は違いを理解していますが、ハッシュが同じことを行うことができ、処理が簡単な場合に、ハッシュよりもStructを使用することの本当の利点は何ですか? Structsは不要なもののようです。

Struct速いです。

require 'benchmark'

Benchmark.bm 10 do |bench|
  bench.report "Hash: " do
    50_000_000.times do { name: "John Smith", age: 45 } end
  end

  bench.report "Struct: " do
    klass = Struct.new(:name, :age)
    50_000_000.times do klass.new("John Smith", 45) end
  end

end

# Ruby 2.2.2p95 (2015-04-13 revision 50295) [x64-mingw32].
#                 user     system      total        real
# Hash:       22.340000   0.016000  22.356000 ( 24.260674)
# Struct:     12.979000   0.000000  12.979000 ( 14.095455)

# Ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin11.0]
# 
#                  user     system      total        real
# Hash:       31.980000   0.060000  32.040000 ( 32.039914)
# Struct:     16.880000   0.010000  16.890000 ( 16.886061)
43
Quv

もう1つの主な違いは、Structに動作メソッドを追加できることです。

 Customer = Struct.new(:name, :address) do

  def greeting; "Hello #{name}!" ; end

end

Customer.new("Dave", "123 Main").greeting  # => "Hello Dave!"
11
Jon

Struct のドキュメントから:

Structは、明示的なクラスを記述せずに、アクセサメソッドを使用して、多数の属性をバンドルする便利な方法です。

一方、 ハッシュ

ハッシュは、キーと値のペアのコレクションです。これは配列に似ていますが、インデックスは整数のインデックスではなく、任意のオブジェクトタイプの任意のキーを介して行われます。キーまたは値のいずれかでハッシュをトラバースする順序は任意に見える可能性があり、通常は挿入順序ではありません。

主な違いは、データへのアクセス方法です。

Ruby-1.9.1-p378 > Point = Struct.new(:x, :y)
 => Point 
Ruby-1.9.1-p378 > p = Point.new(4,5)
 => #<struct Point x=4, y=5> 
Ruby-1.9.1-p378 > p.x
 => 4 
Ruby-1.9.1-p378 > p.y
 => 5 
Ruby-1.9.1-p378 > p = {:x => 4, :y => 5}
 => {:x=>4, :y=>5} 
Ruby-1.9.1-p378 > p.x
NoMethodError: undefined method `x' for {:x=>4, :y=>5}:Hash
    from (irb):7
    from /Users/mr/.rvm/rubies/Ruby-1.9.1-p378/bin/irb:17:in `<main>'
Ruby-1.9.1-p378 > p[:x]
 => 4 
Ruby-1.9.1-p378 > p[:y]
 => 5 

つまり、 "plain old data"構造 のクラスが必要な場合は新しいStructを作成します(オプションで、より多くのメソッドで拡張する目的で)、次の場合はHashを使用します。正式な型はまったく必要ありません。

10
Mark Rushakoff

データをカプセル化するだけの場合は、ハッシュ(またはハッシュの配列)で問題ありません。データで他のデータを操作または操作することを計画している場合、Structはいくつかの興味深い可能性を開くことができます。

Point = Struct.new(:x, :y)
point_a = Point.new(0,0)
point_b = Point.new(2,3)

class Point
  def distance_to another_point
    Math.sqrt((self.x - another_point.x)**2 + (self.y - another_point.y)**2)
  end
end

puts point_a.distance_to point_b