В целом, каковы преимущества и недостатки использования OpenStruct по сравнению со Struct? Какой тип общих вариантов использования будет соответствовать каждому из них?
В целом, каковы преимущества и недостатки использования OpenStruct по сравнению со Struct? Какой тип общих вариантов использования будет соответствовать каждому из них?
Ответы:
С помощью OpenStruct
, вы можете произвольно создавать атрибуты. А Struct
, с другой стороны, его атрибуты должны быть определены при его создании. Выбор одного над другим должен основываться прежде всего на том, нужно ли вам добавлять атрибуты позже.
Подумать о них можно как о среднем уровне спектра между хэшами с одной стороны и классами с другой. Они подразумевают более конкретные отношения между данными, чем a Hash
, но у них нет методов экземпляра, как у класса. Например, множество параметров для функции имеют смысл в хэше; они только слабо связаны. Имя, адрес электронной почты и номер телефона, необходимые для функции, могут быть упакованы вместе в Struct
или OpenStruct
. Если для этого имени, адреса электронной почты и номера телефона требуются методы для предоставления имени в форматах «First Last» и «Last, First», вам следует создать класс для его обработки.
class Point < Struct.new(:x, :y); methods here; end
Point = Struct.new(:x, :y) { methods here }
. ( источник ) Конечно, { ... }
это может быть записано как многострочный блок ( do ... end
), и, я думаю, это предпочтительный способ.
Другой тест:
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)
ОБНОВИТЬ:
Начиная с Ruby 2.4.1 OpenStruct и Struct намного ближе по скорости. См. Https://stackoverflow.com/a/43987844/128421
РАНЕЕ:
Для полноты: Struct против класса против Hash против OpenStruct
Выполнение кода, похожего на burtlo, в Ruby 1.9.2 (1 из 4 ядер x86_64, 8 ГБ ОЗУ) [таблица отредактирована для выравнивания столбцов]:
создание 1 Mio Structs: 1,43 с, 219 МБ / 90 МБ (вирт / разрешение) создание 1 экземпляров класса Mio: 1,43 с, 219 МБ / 90 МБ (вирт / разрешение) создание 1 хеша Mio: 4,46 с, 493 МБ / 364 МБ (вирт / разрешение) создание 1 Mio OpenStructs: 415,13 с, 2464 МБ / 2,3 ГБ (вирт / разрешение) # ~ в 100 раз медленнее, чем хэши создание 100K OpenStructs: 10,96 с, 369 МБ / 242 МБ (вирт / разрешение)
OpenStructs являются sloooooow и большим объемом памяти , и не очень хорошо масштабируются для больших наборов данных
Создание 1 Mio OpenStructs в ~ 100 раз медленнее, чем создание 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"
Варианты использования для этих двух совершенно разные.
Вы можете думать о классе Struct в Ruby 1.9 как о эквиваленте 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 можно использовать для простого преобразования хешей в одноразовые объекты, которые отвечают на все ключи хешей.
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
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 МБ памяти.
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
Посмотрите на API в отношении нового метода. Много различий можно найти там.
Лично мне очень нравится OpenStruct, так как мне не нужно заранее определять структуру объекта, а просто добавлять вещи, как я хочу. Я предполагаю, что это будет его главным (не) преимуществом?
Используя код @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)
Не на самом деле ответ на вопрос, но очень важный момент, если вы заботитесь о производительности . Обратите внимание, что каждый раз, когда вы создаете OpenStruct
операцию, очищается кэш метода, что означает, что ваше приложение будет работать медленнее. Медлительность или нет OpenStruct
заключается не только в том, как она работает сама по себе, но и в том, что их использование приводит ко всему приложению: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that. -clear-rubys-method-cache.md # openstructs