В целом, каковы преимущества и недостатки использования OpenStruct по сравнению с Struct? Какой тип общих вариантов использования подходит каждому из них?
Когда я должен использовать Struct vs. OpenStruct?
Ответ 1
С помощью OpenStruct
вы можете произвольно создавать атрибуты. A Struct
, с другой стороны, должен иметь свои атрибуты, определенные при его создании. Выбор одного над другим должен основываться главным образом на том, нужно ли вам добавлять атрибуты позже.
Способ думать о них - это как промежуточная точка спектра между хэшами с одной стороны и классами - с другой. Они подразумевают более конкретную взаимосвязь между данными, чем a Hash
, но у них нет методов экземпляра, как класс. Например, набор параметров для функции имеет смысл в хеше; они только слабо связаны. Имя, адрес электронной почты и номер телефона, необходимые для функции, могут быть упакованы вместе в Struct
или OpenStruct
. Если это имя, адрес электронной почты и номер телефона нуждаются в методах предоставления имени в форматах "Первый Последний" и "Последний, Первый", тогда вы должны создать класс для его обработки.
Ответ 2
Другой ориентир:
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)
Ответ 3
UPDATE:
Начиная с Ruby 2.4.1 OpenStruct и Struct намного ближе к скорости. См. fooobar.com/questions/39137/...
РАНЕЕ:
Для полноты: Struct и Класс против Хеш против OpenStruct
Запуск аналогичного кода как Burtlo's, на Ruby 1.9.2, (1 из 4 ядер x86_64, 8GB RAM) [таблица отредактирована для выравнивания столбцов]:
creating 1 Mio Structs : 1.43 sec , 219 MB / 90MB (virt/res) creating 1 Mio Class instances : 1.43 sec , 219 MB / 90MB (virt/res) creating 1 Mio Hashes : 4.46 sec , 493 MB / 364MB (virt/res) creating 1 Mio OpenStructs : 415.13 sec , 2464 MB / 2.3GB (virt/res) # ~100x slower than Hashes creating 100K OpenStructs : 10.96 sec , 369 MB / 242MB (virt/res)
OpenStructs sloooooow и с интенсивной памятью, а недостаточно масштабируются для больших наборов данных
Создание 1 Mio OpenStructs ~ 100x медленнее, чем, создавая 1 Mio Хеши.
start = Time.now
collection = (1..10**6).collect do |i|
{:name => "User" , :age => 21}
end; 1
stop = Time.now
puts "#{stop - start} seconds elapsed"
Ответ 4
Варианты использования для этих двух приложений совершенно разные.
Вы можете представить класс Struct в Ruby 1.9 как эквивалент объявления struct
в C. В Ruby Struct.new
берет набор имен полей в качестве аргументов и возвращает новый класс. Аналогично, в C объявление struct
принимает набор полей и позволяет программисту использовать новый сложный тип, как и любой встроенный тип.
Ruby:
Newtype = Struct.new(:data1, :data2)
n = Newtype.new
С
typedef struct {
int data1;
char data2;
} newtype;
newtype n;
Класс OpenStruct можно сравнить с объявлением анонимной структуры в C. Он позволяет программисту создать экземпляр сложного типа.
Ruby:
o = OpenStruct.new(data1: 0, data2: 0)
o.data1 = 1
o.data2 = 2
С
struct {
int data1;
char data2;
} o;
o.data1 = 1;
o.data2 = 2;
Вот несколько распространенных случаев использования.
OpenStructs можно использовать для простого преобразования хэшей в одноразовые объекты, которые отвечают на все хеш-ключи.
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
Ответ 5
OpenStructs использует значительно больший объем памяти и работает медленнее по сравнению с Structs.
require 'ostruct'
collection = (1..100000).collect do |index|
OpenStruct.new(:name => "User", :age => 21)
end
В моей системе следующий код выполнялся за 14 секунд и потреблял 1,5 ГБ памяти. Ваш пробег может отличаться:
User = Struct.new(:name, :age)
collection = (1..100000).collect do |index|
User.new("User",21)
end
Это закончилось почти мгновенно и потребляло 26,6 МБ памяти.
Ответ 6
Посмотрите API на новый метод. Много различий можно найти там.
Лично мне очень нравится OpenStruct, так как мне не нужно заранее определять структуру объекта и просто добавлять вещи, как я хочу. Я полагаю, что это будет его главное преимущество (?)?
Ответ 7
Используя код @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)
Ответ 8
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