StructとOpenStructはどちらを使用すればよいですか?


184

一般的に、Structと比較してOpenStructを使用する利点と欠点は何ですか?これらのそれぞれに適合するのは、どのような一般的なユースケースですか?


1
誰かが興味を持っている場合に備えて、最近のブログのコメント「Structs inside out」で、StructとOpenStructとHashについていくつかコメントがあります。
Robert Klemme、

Hash、Struct、およびOpenStructの速度に関する情報は古くなっています。最近のベンチマークについては、stackoverflow.com / a / 43987844/128421をご覧ください。
Tin Man

回答:


173

を使用するとOpenStruct、任意に属性を作成できます。Struct一方、Aは、作成時に属性を定義する必要があります。どちらを選択するかは、後で属性を追加できるようにする必要があるかどうかに主に基づいて決定する必要があります。

それらについて考える方法は、一方のハッシュともう一方のクラスの間のスペクトルの中間点としてです。これらは、の場合よりもデータ間のより具体的な関係を意味Hashしますが、クラスのようにインスタンスメソッドはありません。たとえば、関数のオプションの束はハッシュで意味をなします。それらは大まかに関連しています。関数に必要な名前、電子メール、および電話番号は、Structまたはにまとめてパッケージ化できますOpenStruct。その名前、電子メール、および電話番号で、「First Last」と「Last、First」の両方の形式で名前を提供するメソッドが必要な場合は、それを処理するクラスを作成する必要があります。


49
「しかし、クラスのようにインスタンスメソッドはありません」。まあ、そこに「ノーマルクラス」としてそれを使用するために、かなり一般的なパターンがある:class Point < Struct.new(:x, :y); methods here; end
tokland

10
@tokland今日のように、メソッドで構造体をカスタマイズする「推奨される」アプローチは、ブロックをコンストラクタに渡すことPoint = Struct.new(:x, :y) { methods here }です。(ソース)もちろん、 { ... }複数行のブロック(do ... end)として記述することもできますが、それが望ましい方法だと思います。
Ivan Kolmychek 2016年

1
@IvanKolmychek:かしこまりました。実際、私はブロックアプローチを好みます。
tokland 2016年

@toklandいいね。コメントが非常に高く投票されているので、より良いアプローチがあることを明確にしたかったので、Rubyを初めて使用する人は実際に「大丈夫です。だから、そうするべきです。誰もがそれに同意するからです」 ?」:)
Ivan Kolmychek 2016年

4
質問:構造体にメソッドを追加したい瞬間に到着したら、クラスを使用しないのはなぜですか?
ジェイデル2017年

82

その他のベンチマーク:

require 'benchmark'
require 'ostruct'

REP = 100000

User = Struct.new(:name, :age)

USER = "User".freeze
AGE = 21
HASH = {:name => USER, :age => AGE}.freeze

Benchmark.bm 20 do |x|
  x.report 'OpenStruct slow' do
    REP.times do |index|
       OpenStruct.new(:name => "User", :age => 21)
    end
  end

  x.report 'OpenStruct fast' do
    REP.times do |index|
       OpenStruct.new(HASH)
    end
  end

  x.report 'Struct slow' do
    REP.times do |index|
       User.new("User", 21)
    end
  end

  x.report 'Struct fast' do
    REP.times do |index|
       User.new(USER, AGE)
    end
  end
end

ベンチマーク結果を自分で実行せずに把握したいせっかちな人のために、上記のコードの出力を以下に示します(MB Pro 2.4GHz i7)。

                          user     system      total        real
OpenStruct slow       4.430000   0.250000   4.680000 (  4.683851)
OpenStruct fast       4.380000   0.270000   4.650000 (  4.649809)
Struct slow           0.090000   0.000000   0.090000 (  0.094136)
Struct fast           0.080000   0.000000   0.080000 (  0.078940)

5
Ruby 2.14の場合、OpenStructを使用した場合の0.94-0.97とOstructを使用した場合の0.02-0.03(MB Pro 2.2Ghz i7)
basex

1
OpenStructの速度は、Structの使用と同等です。stackoverflow.com/a/43987844/128421を参照してください。
ブリキの男

57

更新:

Ruby 2.4.1の時点で、OpenStructとStructの方がはるかに高速です。https://stackoverflow.com/a/43987844/128421を参照してください

以前:

完全性のために:構造体 vs クラス vs ハッシュ vs OpenStruct

Ruby 1.9.2でburtloと同様のコードを実行(4コアのうちの1つx86_64、8 GB RAM)[列を整列するために編集されたテーブル]:

1 Mio Structsの作成:1.43秒、219 MB / 90MB(virt / res)
1 Mioクラスインスタンスの作成:1.43秒、219 MB / 90 MB(仮想/解像度)
1 Mioハッシュの作成:4.46秒、493 MB / 364MB(仮想/解像度)
1 Mio OpenStructsの作成:415.13秒、2,464 MB / 2.3GB(virt / res)#ハッシュより〜100倍遅い
100K OpenStructsの作成:10.96秒、369 MB / 242MB(virt / res)

OpenStructsはありsloooooowメモリ集約、および大規模なデータセットのためにうまく拡張しないでください

1 Mio OpenStructsの作成は、1 Mio ハッシュの作成よりも〜100倍遅くなります

start = Time.now

collection = (1..10**6).collect do |i|
  {:name => "User" , :age => 21}
end; 1

stop = Time.now

puts "#{stop - start} seconds elapsed"

私のようなパフォーマンス中毒者のための非常に有用な情報。ありがとう。
ベルナルドオリベイラ

私は、Ruby(MRI)のマッツの実装を参照しています
は、Tilo

1
こんにちは@Tilo、コードを共有して上記の結果を得ることができますか?Struct&OStructとHashie :: Mashを比較するために使用したいと思います。ありがとう。
Donny Kurnia 14年

1
@Donnyさん、賛成票を見たところ、2011年に測定されたことがわかりました。Ruby2.1でこれを再実行する必要があります。P:そのコードがあるかどうかはわかりませんが、簡単に再現できます。すぐに修正しようと思います。
Tilo

2
Ruby 2.4.1の時点で、OpenStructとStructの方がはるかに高速です。stackoverflow.com/a/43987844/128421を
Tin Man

34

2つのユースケースはかなり異なります。

Ruby 1.9のStructクラスはstruct、C の宣言に相当すると考えることができます。Ruby Struct.newでは、引数としてフィールド名のセットを取り、新しいクラスを返します。同様に、Cでは、struct宣言は一連のフィールドを取り、プログラマーが組み込み型と同じように新しい複合型を使用できるようにします。

ルビー:

Newtype = Struct.new(:data1, :data2)
n = Newtype.new

C:

typedef struct {
  int data1;
  char data2;
} newtype;

newtype n;

OpenStructクラスは、Cの匿名構造体宣言と比較できます。これにより、プログラマーは複合型のインスタンスを作成できます

ルビー:

o = OpenStruct.new(data1: 0, data2: 0) 
o.data1 = 1
o.data2 = 2

C:

struct {
  int data1;
  char data2;
} o;

o.data1 = 1;
o.data2 = 2;

一般的な使用例をいくつか示します。

OpenStructsを使用すると、ハッシュを、すべてのハッシュキーに応答する1回限りのオブジェクトに簡単に変換できます。

h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2

構造体は、簡単なクラス定義に役立ちます。

class MyClass < Struct.new(:a,:b,:c)
end

m = MyClass.new
m.a = 1

3
これは、両者の概念的な違いに対する素晴らしい答えです。OpenStructの匿名性を指摘していただきありがとうございます。それにより、より明確になります。
ブライアント、2014年

素晴らしい説明!
Yuri Ghensev

24

OpenStructsはかなり多くのメモリを使用し、Structsに比べてパフォーマンスが低下します。

require 'ostruct' 

collection = (1..100000).collect do |index|
   OpenStruct.new(:name => "User", :age => 21)
end

私のシステムでは、次のコードが14秒で実行され、1.5 GBのメモリを消費しました。あなたの走行距離は異なる場合があります:

User = Struct.new(:name, :age)

collection = (1..100000).collect do |index|
   User.new("User",21)
end

これはほぼ瞬時に終了し、26.6 MBのメモリを消費しました。


3
しかし、あなたはOpenStructテストが多くの一時的なハッシュを作成することを知っています。わずかに変更されたベンチマークをお勧めします-それでもあなたの評決をサポートします(以下を参照)。
Robert Klemme、

6

Struct

>> s = Struct.new(:a, :b).new(1, 2)
=> #<struct a=1, b=2>
>> s.a
=> 1
>> s.b
=> 2
>> s.c
NoMethodError: undefined method `c` for #<struct a=1, b=2>

OpenStruct

>> require 'ostruct'
=> true
>> os = OpenStruct.new(a: 1, b: 2)
=> #<OpenStruct a=1, b=2>
>> os.a
=> 1
>> os.b
=> 2
>> os.c
=> nil

例をありがとう。それは実際に理解するのに大いに役立ちます。
Ahsan

5

新しいメソッドについては、APIをご覧ください。そこには多くの違いがあります。

個人的には、オブジェクトの構造を事前に定義する必要がないので、OpenStructが非常に好きです。必要なだけのものを追加するだけです。それが主な(不利な)利点になると思いますか?


3

@Robertコードを使用して、Hashie :: Mashをベンチマーク項目に追加し、次の結果を得ました。

                           user     system      total        real
Hashie::Mash slow      3.600000   0.000000   3.600000 (  3.755142)
Hashie::Mash fast      3.000000   0.000000   3.000000 (  3.318067)
OpenStruct slow       11.200000   0.010000  11.210000 ( 12.095004)
OpenStruct fast       10.900000   0.000000  10.900000 ( 12.669553)
Struct slow            0.370000   0.000000   0.370000 (  0.470550)
Struct fast            0.140000   0.000000   0.140000 (  0.145161)

あなたのベンチマークは本当に奇妙です。私は、i5 Mac上ruby2.1.1と、次の結果を得た:gist.github.com/nicolas-besnard/...
cappie013

まあ、結果は使用されているルビのバージョンとそれを実行するために使用されたハードウェアとの間で異なります。しかし、パターンは同じです。OpenStructが最も遅く、Structが最も高速です。はしえが真ん中に落ちる。
Donny Kurnia 14

0

実際には質問に対する回答ではありませんが、パフォーマンスを重視する場合は非常に重要な考慮事項ですOpenStruct操作を作成するたびにメソッドキャッシュがクリアされることに注意してください。これは、アプリケーションのパフォーマンスが低下することを意味します。遅いかOpenStructどうかは、それ自体がどのように機能するかだけでなく、それらを使用することがアプリケーション全体にもたらす影響です:https : //github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md#openstructs

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.