Jakie są zalety i wady korzystania z OpenStruct w porównaniu do Struct? Jaki rodzaj ogólnych przypadków użycia pasowałby do każdego z nich?
Jakie są zalety i wady korzystania z OpenStruct w porównaniu do Struct? Jaki rodzaj ogólnych przypadków użycia pasowałby do każdego z nich?
Odpowiedzi:
Za pomocą OpenStruct
możesz dowolnie tworzyć atrybuty. Z Struct
drugiej strony, podczas tworzenia należy zdefiniować jego atrybuty. Wybór jednego z pozostałych powinien opierać się przede wszystkim na tym, czy trzeba będzie później dodać atrybuty.
Sposób myślenia o nich jest środkiem pola między Hashes z jednej strony i klasami z drugiej. Implikują one bardziej konkretny związek między danymi niż a Hash
, ale nie mają metod instancji, tak jak klasa. Na przykład wiele opcji dla funkcji ma sens w haszowaniu; są tylko luźno spokrewnione. Imię, adres e-mail i numer telefonu wymagane przez funkcję mogą być spakowane razem w Struct
lub OpenStruct
. Jeśli ta nazwa, adres e-mail i numer telefonu wymagały metod, aby podać nazwę zarówno w formacie „First Last”, jak i „Last, First”, należy utworzyć klasę do obsługi tej nazwy.
class Point < Struct.new(:x, :y); methods here; end
Point = Struct.new(:x, :y) { methods here }
. ( źródło ) Oczywiście, { ... }
można napisać jako blok wieloliniowy ( do ... end
) i myślę, że jest to preferowany sposób.
Inne punkty odniesienia:
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
Dla niecierpliwych, którzy chcą zorientować się w wynikach testu, bez uruchamiania ich samych, oto wynik powyższego kodu (na 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)
AKTUALIZACJA:
Począwszy od Ruby 2.4.1 OpenStruct i Struct są znacznie szybsze. Zobacz https://stackoverflow.com/a/43987844/128421
POPRZEDNIO:
Dla kompletności: Struct vs. Class vs. Hash vs. OpenStruct
Uruchamianie kodu podobnego do burtlo na Ruby 1.9.2, (1 z 4 rdzeni x86_64, 8 GB RAM) [tabela edytowana w celu wyrównania kolumn]:
tworzenie 1 Mio Struktur: 1,43 s, 219 MB / 90 MB (virt / res) tworzenie instancji 1 Mio Class: 1,43 s, 219 MB / 90 MB (virt / res) tworzenie 1 Mio Hashes: 4,46 s, 493 MB / 364 MB (virt / res) tworzenie 1 Mio OpenStructs: 415,13 s, 2464 MB / 2,3 GB (virt / res) # ~ 100x wolniej niż Hashes tworzenie 100K OpenStructs: 10,96 s, 369 MB / 242 MB (virt / res)
OpenStructs są bardzo wolne i wymagają dużej ilości pamięci i nie skalują się dobrze dla dużych zestawów danych
Utworzenie 1 Mio OpenStruct jest ~ 100x wolniejsze niż utworzenie 1 Mio Hashes .
start = Time.now
collection = (1..10**6).collect do |i|
{:name => "User" , :age => 21}
end; 1
stop = Time.now
puts "#{stop - start} seconds elapsed"
Przypadki użycia tych dwóch elementów są zupełnie inne.
Możesz myśleć o klasie Struct w Ruby 1.9 jako ekwiwalencie struct
deklaracji w C. W Ruby Struct.new
bierze zestaw nazw pól jako argumenty i zwraca nową klasę. Podobnie w C struct
deklaracja przyjmuje zestaw pól i pozwala programiście używać nowego typu złożonego, tak jak każdego wbudowanego typu.
Rubin:
Newtype = Struct.new(:data1, :data2)
n = Newtype.new
DO:
typedef struct {
int data1;
char data2;
} newtype;
newtype n;
Klasę OpenStruct można porównać do anonimowej deklaracji struktury w C. Pozwala to programiście utworzyć instancję typu złożonego.
Rubin:
o = OpenStruct.new(data1: 0, data2: 0)
o.data1 = 1
o.data2 = 2
DO:
struct {
int data1;
char data2;
} o;
o.data1 = 1;
o.data2 = 2;
Oto kilka typowych przypadków użycia.
OpenStructs może być używany do łatwej konwersji skrótów na jednorazowe obiekty, które reagują na wszystkie klucze skrótu.
h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2
Struktury mogą być przydatne do definicji klas skrótowych.
class MyClass < Struct.new(:a,:b,:c)
end
m = MyClass.new
m.a = 1
OpenStruct zużywa znacznie więcej pamięci i działa wolniej w porównaniu do Struct.
require 'ostruct'
collection = (1..100000).collect do |index|
OpenStruct.new(:name => "User", :age => 21)
end
W moim systemie następujący kod został wykonany w ciągu 14 sekund i zużył 1,5 GB pamięci. Twój przebieg może się różnić:
User = Struct.new(:name, :age)
collection = (1..100000).collect do |index|
User.new("User",21)
end
To zakończyło się niemal natychmiast i zużyło 26,6 MB pamięci.
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
Zobacz API w odniesieniu do nowej metody. Można tam znaleźć wiele różnic.
Osobiście bardzo podoba mi się OpenStruct, ponieważ nie muszę wcześniej definiować struktury obiektu i po prostu dodawać rzeczy, jak chcę. Myślę, że to byłaby jego główna (nie) zaleta?
Używając kodu @Robert, dodaję Hashie :: Mash do elementu testu i otrzymałem ten wynik:
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)
Nie jest to właściwie odpowiedź na pytanie, ale bardzo ważna uwaga, jeśli zależy Ci na wydajności . Zauważ, że za każdym razem, gdy tworzysz OpenStruct
operację, pamięć podręczna metod jest czyszczona, co oznacza, że aplikacja będzie działała wolniej. Powolność, czy nie, nie OpenStruct
polega tylko na tym, jak to działa sama, ale implikacje, które przynoszą ich zastosowanie dla całej aplikacji: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs