Загалом, які переваги та недоліки використання OpenStruct порівняно зі структурою? Який тип випадків загального користування підходив би до кожного з них?
Загалом, які переваги та недоліки використання OpenStruct порівняно зі структурою? Який тип випадків загального користування підходив би до кожного з них?
Відповіді:
За допомогою OpenStruct
, ви можете довільно створювати атрибути. А Struct
, з іншого боку, повинні бути визначені його атрибути, коли ви створюєте їх. Вибір одного над іншим повинен ґрунтуватися насамперед на тому, чи потрібно мати змогу додавати атрибути пізніше.
Спосіб подумати про них - це середина спектру між хешами з одного боку та класами з іншого. Вони мають на увазі більш конкретну залежність між даними, ніж у a Hash
, але у них немає методів екземпляра, як у класу. Купа варіантів функції, наприклад, має сенс у хеші; вони лише слабко пов'язані. Ім'я, електронна адреса та номер телефону, необхідні функції, можуть бути упаковані разом у Struct
або OpenStruct
. Якщо для цього імені, електронної пошти та номера телефону потрібні способи надання імені у форматах "Перше останнє" та "Останнє, перше", тоді вам слід створити клас для його обробки.
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
ПОПЕРЕДНЯ:
Для повноти: Структура проти Class vs. Hash vs. OpenStruct
Запуск аналогічного коду, як burtlo, на Ruby 1.9.2, (1 з 4 ядер x86_64, 8 Гб оперативної пам’яті) [таблиця відредагована для вирівнювання стовпців]:
створення 1 Міо структури: 1,43 сек, 219 МБ / 90 МБ (virt / res) створення екземплярів 1 класу Mio: 1,43 сек, 219 МБ / 90 МБ (virt / res) створення 1 Міо хешей: 4,46 сек, 493 МБ / 364 МБ (virt / res) створення 1 Mio OpenStructs: 415,13 сек, 2464 МБ / 2,3 ГБ (virt / res) # ~ 100 разів повільніше, ніж хеші створення 100K OpenStructs: 10,96 сек, 369 МБ / 242 МБ (virt / res)
OpenStructs є sloooooow і великим об'ємом пам'яті , і НЕ дуже добре масштабуються для великих наборів даних
Створення 1 Міо OpenStructs на 100 разів повільніше, ніж створення 1 Міо хешів .
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
приймається набір полів імен як аргументів і повертає новий Class. Аналогічно, в C struct
декларація приймає набір полів і дозволяє програмісту використовувати новий складний тип так само, як і будь-який вбудований тип.
Ruby:
Newtype = Struct.new(:data1, :data2)
n = Newtype.new
C:
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
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