Ne zaman Struct ve OpenStruct'ı kullanmalıyım?


184

Genel olarak, bir Yapıyla karşılaştırıldığında OpenStruct kullanmanın avantajları ve dezavantajları nelerdir? Bunların her birine ne tür genel kullanım durumları uygundur?


1
Birisi ilgileniyorsa, benim son blog yorumumda "Yapılar dışarıda" Yapısı vs OpenStruct vs Hash hakkında birkaç söz var .
Robert Klemme

Hash, Struct ve OpenStruct'ın hızı ile ilgili bilgiler güncel değil. Bkz stackoverflow.com/a/43987844/128421 daha yeni kriter için.
Tin Man

Yanıtlar:


173

İle OpenStruct, keyfi olarak özellikler oluşturabilirsiniz. StructÖte yandan, A'yı oluştururken tanımlanmış niteliklere sahip olmalıdır. Birinin diğerinin seçimi, daha sonra nitelik ekleyebilmeniz gerekip gerekmediğine dayanmalıdır.

Bunları düşünmenin yolu, bir taraftaki Hashes ile diğer taraftaki sınıflar arasındaki spektrumun orta zeminidir. Veriler arasında a'dan daha somut bir ilişki olduğunu ima ederler Hash, ancak sınıfta olduğu gibi örnek yöntemlere sahip değildirler. Örneğin, bir işlev için bir grup seçenek bir karma içinde mantıklıdır; sadece gevşek ilişkilidirler. Bir işlevin ihtiyaç duyduğu bir ad, e-posta ve telefon numarası bir Structveya içinde birlikte paketlenebilir OpenStruct. Bu ad, e-posta ve telefon numarası, adı hem "İlk Son" hem de "Son, İlk" biçiminde sağlamak için yöntemlere gereksinim duyuyorsa, bunu işlemek için bir sınıf oluşturmanız gerekir.


49
"ancak sınıf gibi örnek yöntemleri yoktur". iyi, "normal sınıf" olarak kullanmak için oldukça yaygın bir desen var:class Point < Struct.new(:x, :y); methods here; end
tokland

10
@tokland bugün olduğu gibi, yapıyı yöntemlerle özelleştirmenin "tercih edilen" yaklaşımı bloğu kurucuya geçirmektir Point = Struct.new(:x, :y) { methods here }. ( kaynak ) Tabii ki, { ... }çok satırlı bir blok ( do ... end) olarak yazılabilir ve bence tercih edilen yol budur.
Ivan Kolmychek

1
@IvanKolmychek: Güzel, aslında blok yaklaşımını tercih ediyorum.
tokland

@tokland iyi. Sadece daha hoş bir yaklaşım olduğunu açıklığa kavuşturmak istedim, yorumunuzun yüksek oy verildiğini görerek, yakutta yeni olan insanlar aslında "Tamam, işte böyle yapılmalı, çünkü herkes buna katılıyor, çünkü doğru" ?" :)
Ivan Kolmychek

4
Bir soru: Yapınıza yöntemler eklemek istediğiniz ana geldiğinizde neden bir sınıf kullanmıyorsunuz?
jaydel

82

Diğer kriter:

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

Kıyaslama sonuçları hakkında bir fikir edinmek isteyen sabırsızlar için, kendilerini çalıştırmadan, yukarıdaki kodun çıktısı (MB Pro 2.4GHz i7'de)

                          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 ile fark OpenStruct ile 0.94-0.97 daha küçüktür ve Ostruct ile 0.02-0.03 (MB Pro 2.2Ghz i7)
basex

1
OpenStruct, Struct'ı kullanma hızıyla eşdeğerdir. Bkz. Stackoverflow.com/a/43987844/128421 .
Teneke Adam

57

GÜNCELLEME:

Ruby 2.4.1'den itibaren OpenStruct ve Struct hızlarına çok daha yakın. Bkz. Https://stackoverflow.com/a/43987844/128421

ÖNCEDEN:

Tamlık için: Struct - Class ve Hash vs. OpenStruct

Ruby 1.9.2'de burtlo's ile benzer kodu çalıştırıyor (4 çekirdekten 1 x86_64, 8GB RAM) [tablo sütunları hizalamak için düzenlendi]:

1 Mio Yapısı oluşturma: 1,43 sn, 219 MB / 90 MB (virt / res)
1 Mio Sınıfı örneği oluşturma: 1,43 sn, 219 MB / 90 MB (virt / res)
1 Mio Karması oluşturma: 4.46 sn, 493 MB / 364MB (virt / res)
1 Mio OpenStructs oluşturma: 415.13 sn, 2464 MB / 2.3 GB (virt / res) Hashes'ten ~ 100 kat daha yavaş
100K OpenStructs oluşturma: 10.96 sn, 369 MB / 242MB (virt / res)

OpenStructs olan sloooooow ve yoğun bellek ve büyük veri kümeleri için de ölçekli değil

1 Mio OpenStructs Oluşturma olduğu daha yavaş 100x 1 Mio oluşturarak karmaları .

start = Time.now

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

stop = Time.now

puts "#{stop - start} seconds elapsed"

Benim gibi performans bağımlıları için çok faydalı bilgiler. Teşekkürler.
Bernardo Oliveira


1
Merhaba @Tilo, yukarıdaki sonuçları almak için kodunuzu paylaşabilir misiniz? Struct & OStruct ile Hashie :: Mash'i karşılaştırmak için kullanmak istiyorum. Teşekkürler.
Donny Kurnia

1
Hey @Donny, sadece upvote gördüm ve bunun 2011 yılında ölçüldüğünü fark ettim - Ruby 2.1: P ile yeniden çalıştırmam gerekiyor, bu koda sahip olduğumdan emin değilim, ancak çoğaltmak basit olmalı. Yakında düzeltmeye çalışacağım.
Tilo

2
Ruby 2.4.1'den itibaren OpenStruct ve Struct hızlarına çok daha yakın. Bkz. Stackoverflow.com/a/43987844/128421
Tin Man

34

İkisi için kullanım durumları oldukça farklıdır.

Ruby 1.9'daki Struct sınıfını structC'deki bildirime eşdeğer olarak düşünebilirsiniz. Ruby'de Struct.newbir alan adları kümesini bağımsız değişken olarak alır ve yeni bir Sınıf döndürür. Benzer şekilde, C'de bir structbildirim bir dizi alanı alır ve programcının yeni karmaşık türü tıpkı herhangi bir yerleşik türde olduğu gibi kullanmasına izin verir.

Yakut:

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

C:

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

newtype n;

OpenStruct sınıfı, C'deki anonim bir yapı bildirimi ile karşılaştırılabilir. Bu, programcının karmaşık türde bir örnek oluşturmasını sağlar .

Yakut:

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;

İşte bazı yaygın kullanım örnekleri.

OpenStructs, karmaları tüm karma anahtarlarına yanıt veren tek seferlik nesnelere kolayca dönüştürmek için kullanılabilir.

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

Yapılar steno sınıf tanımları için yararlı olabilir.

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

m = MyClass.new
m.a = 1

3
Bu, aralarındaki kavramsal farkın harika bir cevabıdır. OpenStruct'ın anonimliğine dikkat çektiğiniz için teşekkürler, bunu çok daha açık hale getiriyor gibi hissediyorum.
bryant

Harika bir açıklama!
Yuri Ghensev

24

OpenStructs, Yapılar'a göre önemli ölçüde daha fazla bellek kullanır ve daha yavaş performans gösterir.

require 'ostruct' 

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

Sistemimde aşağıdaki kod 14 saniye içinde yürütüldü ve 1,5 GB bellek tüketti. Kilometreniz değişebilir:

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

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

Bu neredeyse anında bitti ve 26.6 MB bellek tüketti.


3
Ancak OpenStruct testinin çok fazla geçici karmalar oluşturduğunun farkındasınız. Kararınızı hala destekleyen biraz değiştirilmiş bir kıyaslama öneriyorum (aşağıya bakın).
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

Örnek için teşekkürler. Uygulamada anlamak çok yardımcı olur.
Ahsan


3

@Robert kodunu kullanarak, karşılaştırma öğesine Hashie :: Mash'i ekledim ve bu sonucu aldım:

                           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)

Karşılaştırmanız gerçekten tuhaf. İ5 mac üzerinde ruby2.1.1 ile şu sonucu aldım: gist.github.com/nicolas-besnard/…
cappie013 17:14

Sonuç, kullanılan yakut sürümü ile çalıştırmak için kullanılan donanım arasında değişiklik gösterir. Ancak desen hala aynı, OpenStruct en yavaş, Struct en hızlı. Hashie ortada düşüyor.
Donny Kurnia

0

Aslında soruya bir cevap değil, performansı önemsiyorsanız çok önemli bir husustur . Her OpenStructişlem oluşturduğunuzda , yöntem önbelleğini temizlediğini, yani uygulamanızın daha yavaş çalışacağını unutmayın. Yavaşlık ya da değil, OpenStructsadece kendi başına nasıl çalıştığı ile ilgili değildir, aynı zamanda bunları kullanmanın tüm uygulamaya getirdiği sonuçlar : https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.