Welche Vor- und Nachteile hat die Verwendung eines OpenStruct im Vergleich zu einer Struktur im Allgemeinen? Welche Art von allgemeinen Anwendungsfällen würde zu jedem dieser Fälle passen?
Welche Vor- und Nachteile hat die Verwendung eines OpenStruct im Vergleich zu einer Struktur im Allgemeinen? Welche Art von allgemeinen Anwendungsfällen würde zu jedem dieser Fälle passen?
Antworten:
Mit einem OpenStruct
können Sie beliebig Attribute erstellen. Bei A Struct
hingegen müssen die Attribute beim Erstellen definiert werden. Die Auswahl eines über das andere sollte in erster Linie davon abhängen, ob Sie später Attribute hinzufügen müssen.
Die Art, über sie nachzudenken, ist der Mittelweg des Spektrums zwischen Hashes auf der einen Seite und Klassen auf der anderen Seite. Sie implizieren eine konkretere Beziehung zwischen den Daten als a Hash
, haben jedoch nicht die Instanzmethoden wie eine Klasse. Eine Reihe von Optionen für eine Funktion sind beispielsweise in einem Hash sinnvoll. Sie sind nur lose verwandt. Ein Name, eine E-Mail-Adresse und eine Telefonnummer, die von einer Funktion benötigt werden, können in einem Struct
oder zusammengefasst werden OpenStruct
. Wenn für diesen Namen, diese E-Mail-Adresse und diese Telefonnummer Methoden erforderlich sind, um den Namen in den Formaten "First Last" und "Last, First" bereitzustellen, sollten Sie eine Klasse erstellen, um damit umzugehen.
class Point < Struct.new(:x, :y); methods here; end
Point = Struct.new(:x, :y) { methods here }
. ( Quelle ) Natürlich { ... }
kann es als mehrzeiliger Block ( do ... end
) geschrieben werden, und ich denke, das ist der bevorzugte Weg.
Andere Benchmark:
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
Für Ungeduldige, die sich ein Bild von den Benchmark-Ergebnissen machen möchten, ohne sie selbst auszuführen, ist hier die Ausgabe des obigen Codes (auf einem 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)
AKTUALISIEREN:
Ab Ruby 2.4.1 sind OpenStruct und Struct viel schneller. Siehe https://stackoverflow.com/a/43987844/128421
VORHER:
Der Vollständigkeit halber : Struct vs. Class vs. Hash vs. OpenStruct
Ausführen von ähnlichem Code wie bei burtlo unter Ruby 1.9.2 (1 von 4 Kernen x86_64, 8 GB RAM) [Tabelle bearbeitet, um Spalten auszurichten]:
Erstellen von 1 Mio. Strukturen: 1,43 Sek., 219 MB / 90 MB (virt / res) Erstellen von Instanzen der 1-Mio-Klasse: 1,43 Sek., 219 MB / 90 MB (virt / res) Erstellen von 1 Mio Hashes: 4,46 Sek., 493 MB / 364 MB (virt / res) Erstellen von 1 Mio OpenStructs: 415,13 Sek., 2464 MB / 2,3 GB (virt / res) # ~ 100x langsamer als Hashes Erstellen von 100K OpenStructs: 10,96 Sek., 369 MB / 242 MB (virt / res)
OpenStructs sind langsam und speicherintensiv und lassen sich für große Datenmengen nicht gut skalieren
Das Erstellen von 1 Mio OpenStructs ist ~ 100x langsamer als das Erstellen von 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"
Die Anwendungsfälle für die beiden sind sehr unterschiedlich.
Sie können sich die Struct-Klasse in Ruby 1.9 als Äquivalent zur struct
Deklaration in C vorstellen . In Ruby werden Struct.new
eine Reihe von Feldnamen als Argumente verwendet und eine neue Klasse zurückgegeben. In ähnlicher Weise struct
nimmt eine Deklaration in C eine Reihe von Feldern auf und ermöglicht es dem Programmierer, den neuen komplexen Typ wie jeden integrierten Typ zu verwenden.
Rubin:
Newtype = Struct.new(:data1, :data2)
n = Newtype.new
C:
typedef struct {
int data1;
char data2;
} newtype;
newtype n;
Die OpenStruct-Klasse kann mit einer anonymen Strukturdeklaration in C verglichen werden. Dadurch kann der Programmierer eine Instanz eines komplexen Typs erstellen .
Rubin:
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;
Hier sind einige häufige Anwendungsfälle.
OpenStructs können verwendet werden, um Hashes einfach in einmalige Objekte zu konvertieren, die auf alle Hash-Schlüssel reagieren.
h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2
Strukturen können für Kurzklassendefinitionen nützlich sein.
class MyClass < Struct.new(:a,:b,:c)
end
m = MyClass.new
m.a = 1
OpenStructs verbrauchen deutlich mehr Speicher und sind langsamer als Structs.
require 'ostruct'
collection = (1..100000).collect do |index|
OpenStruct.new(:name => "User", :age => 21)
end
Auf meinem System wurde der folgende Code in 14 Sekunden ausgeführt und verbrauchte 1,5 GB Speicher. Ihr Kilometerstand kann variieren:
User = Struct.new(:name, :age)
collection = (1..100000).collect do |index|
User.new("User",21)
end
Das endete fast augenblicklich und verbrauchte 26,6 MB Speicher.
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
Schauen Sie sich die API bezüglich der neuen Methode an. Viele der Unterschiede sind dort zu finden.
Persönlich mag ich OpenStruct sehr, da ich die Struktur des Objekts nicht vorher definieren und einfach so hinzufügen muss, wie ich möchte. Ich denke, das wäre der Hauptvorteil?
Mit @ Robert-Code füge ich Hashie :: Mash zum Benchmark-Element hinzu und erhalte das folgende Ergebnis:
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)
Eigentlich keine Antwort auf die Frage, aber eine sehr wichtige Überlegung, wenn Sie Wert auf Leistung legen . Bitte beachten Sie, dass jedes Mal, wenn Sie einen OpenStruct
Vorgang erstellen, der Methodencache geleert wird, was bedeutet, dass Ihre Anwendung langsamer arbeitet. Bei der Langsamkeit oder Nicht-Langsamkeit OpenStruct
geht es nicht nur darum, wie es von selbst funktioniert, sondern auch um die Auswirkungen , die die Verwendung auf die gesamte Anwendung hat: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs