Ruby Struct允许使用一组访问器生成实例:
# Create a structure named by its constant
Customer = Struct.new(:name, :address) #=> Customer
Customer.new("Dave", "123 Main") #=> #<Customer name="Dave", address="123 Main">
这看起来既方便又强大,但是Hash做了类似的事情:
Customer = {:name => "Dave", :address => "123 Main"}
在什么样的情况下,我更喜欢结构(以及为什么),以及选择其中一个的警告或陷阱是什么?
答案 0 :(得分:21)
就个人而言,当我想让一段数据像一个数据集合而不是松散地耦合在Hash
下时,我会使用一个结构。
例如,我制作了一个从Youtube下载视频的脚本,在那里我有一个表示视频的结构,并测试是否所有数据都已到位:
Video = Struct.new(:title, :video_id, :id) do
def to_s
"http://youtube.com/get_video.php?t=#{id}&video_id=#{video_id}&fmt=18"
end
def empty?
@title.nil? and @video_id.nil? and @id.nil?
end
end
稍后在我的代码中,我有一个循环遍历视频源HTML页面中的所有行,直到empty?
不返回true。
我见过的另一个例子是James Edward Gray IIs configuration class,它使用OpenStruct
轻松添加从外部文件加载的配置变量:
#!/usr/bin/env ruby -wKU
require "ostruct"
module Config
module_function
def load_config_file(path)
eval <<-END_CONFIG
config = OpenStruct.new
#{File.read(path)}
config
END_CONFIG
end
end
# configuration_file.rb
config.db = File.join(ENV['HOME'], '.cool-program.db')
config.user = ENV['USER']
# Usage:
Config = Config.load_config('configuration_file.rb')
Config.db # => /home/ba/.cool-program.db
Config.user # => ba
Config.non_existant # => Nil
Struct
和OpenStruct
之间的区别在于Struct
仅响应您设置的属性,OpenStruct
响应任何属性集 - 但没有响应任何属性集值集将返回Nil
答案 1 :(得分:10)
Struct具有以下功能:您可以通过索引和名称获取其元素:
irb(main):004:0> Person = Struct.new(:name, :age)
=> Person
irb(main):005:0> p = Person.new("fred", 26)
=> #
irb(main):006:0> p[0]
=> "fred"
irb(main):007:0> p[1]
=> 26
irb(main):008:0> p.name
=> "fred"
irb(main):009:0> p.age
=> 26
有时很有用。
答案 2 :(得分:6)
主要是表现。按大小顺序,结构更快。与Hash或OpenStruct相比,消耗的内存更少。更多信息:When should I use Struct vs. OpenStruct?
答案 3 :(得分:4)
关于使用Hashes,Struct或OpenStruct的速度的评论:Hash总是赢得一般用途。它是OpenStruct的基础,没有额外的结冰,所以它不那么灵活,但它很精简和卑鄙。
使用Ruby 2.4.1:
require 'fruity'
require 'ostruct'
def _hash
h = {}
h['a'] = 1
h['a']
end
def _struct
s = Struct.new(:a)
foo = s.new(1)
foo.a
end
def _ostruct
person = OpenStruct.new
person.a = 1
person.a
end
compare do
a_hash { _hash }
a_struct { _struct }
an_ostruct { _ostruct }
end
# >> Running each test 4096 times. Test will take about 2 seconds.
# >> a_hash is faster than an_ostruct by 13x ± 1.0
# >> an_ostruct is similar to a_struct
使用更简洁的散列和OpenStruct定义:
require 'fruity'
require 'ostruct'
def _hash
h = {'a' => 1}
h['a']
end
def _struct
s = Struct.new(:a)
foo = s.new(1)
foo.a
end
def _ostruct
person = OpenStruct.new('a' => 1)
person.a
end
compare do
a_hash { _hash }
a_struct { _struct }
an_ostruct { _ostruct }
end
# >> Running each test 4096 times. Test will take about 2 seconds.
# >> a_hash is faster than an_ostruct by 17x ± 10.0
# >> an_ostruct is similar to a_struct
如果结构,Hash或Struct或OpenStruct被定义一次然后多次使用,那么访问速度变得更加重要,并且Struct开始闪耀:
require 'fruity'
require 'ostruct'
HSH = {'a' => 1}
def _hash
HSH['a']
end
STRCT = Struct.new(:a).new(1)
def _struct
STRCT.a
end
OSTRCT = OpenStruct.new('a' => 1)
def _ostruct
OSTRCT.a
end
puts "Ruby version: #{RUBY_VERSION}"
compare do
a_hash { _hash }
a_struct { _struct }
an_ostruct { _ostruct }
end
# >> Ruby version: 2.4.1
# >> Running each test 65536 times. Test will take about 2 seconds.
# >> a_struct is faster than a_hash by 4x ± 1.0
# >> a_hash is similar to an_ostruct
请注意,Struct的访问速度仅比Hash快4倍,而Hash的初始化,分配和访问速度快17倍。您必须根据特定应用程序的需要确定哪种方法最佳使用。因此,我倾向于使用哈希作为一般用途。
此外,多年来使用OpenStruct的速度也有了很大提高;它曾经比我过去看到的基准测试结构慢,而且与1.9.3-p551比较:
require 'fruity'
require 'ostruct'
def _hash
h = {}
h['a'] = 1
h['a']
end
def _struct
s = Struct.new(:a)
foo = s.new(1)
foo.a
end
def _ostruct
person = OpenStruct.new
person.a = 1
person.a
end
puts "Ruby version: #{RUBY_VERSION}"
compare do
a_hash { _hash }
a_struct { _struct }
an_ostruct { _ostruct }
end
# >> Ruby version: 1.9.3
# >> Running each test 4096 times. Test will take about 2 seconds.
# >> a_hash is faster than a_struct by 7x ± 1.0
# >> a_struct is faster than an_ostruct by 2x ± 0.1
和
require 'fruity'
require 'ostruct'
def _hash
h = {'a' => 1}
h['a']
end
def _struct
s = Struct.new(:a)
foo = s.new(1)
foo.a
end
def _ostruct
person = OpenStruct.new('a' => 1)
person.a
end
puts "Ruby version: #{RUBY_VERSION}"
compare do
a_hash { _hash }
a_struct { _struct }
an_ostruct { _ostruct }
end
# >> Ruby version: 1.9.3
# >> Running each test 4096 times. Test will take about 2 seconds.
# >> a_hash is faster than a_struct by 7x ± 1.0
# >> a_struct is faster than an_ostruct by 2x ± 1.0
和
require 'fruity'
require 'ostruct'
HSH = {'a' => 1}
def _hash
HSH['a']
end
STRCT = Struct.new(:a).new(1)
def _struct
STRCT.a
end
OSTRCT = OpenStruct.new('a' => 1)
def _ostruct
OSTRCT.a
end
puts "Ruby version: #{RUBY_VERSION}"
compare do
a_hash { _hash }
a_struct { _struct }
an_ostruct { _ostruct }
end
# >> Ruby version: 1.9.3
# >> Running each test 32768 times. Test will take about 1 second.
# >> a_struct is faster than an_ostruct by 3x ± 1.0
# >> an_ostruct is similar to a_hash
答案 4 :(得分:2)
对于喜欢IPS(每秒迭代)指标的用户来说,以下基准更具可读性:
require 'benchmark/ips'
require 'ostruct'
MyStruct = Struct.new(:a)
Benchmark.ips do |x|
x.report('hash') { a = { a: 1 }; a[:a] }
x.report('struct') { a = MyStuct.new(1); a.a }
x.report('ostruct') { a = OpenStruct.new(a: 1); a.a }
x.compare!
end
Warming up --------------------------------------
hash 147.162k i/100ms
struct 171.949k i/100ms
ostruct 21.086k i/100ms
Calculating -------------------------------------
hash 2.608M (± 3.1%) i/s - 13.097M in 5.028022s
struct 3.680M (± 1.8%) i/s - 18.399M in 5.001510s
ostruct 239.108k (± 5.5%) i/s - 1.202M in 5.046817s
Comparison:
struct: 3679772.2 i/s
hash: 2607565.1 i/s - 1.41x slower
ostruct: 239108.4 i/s - 15.39x slower
require 'benchmark/ips'
require 'ostruct'
MyStruct = Struct.new(:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m, :n, :o, :p, :q, :r, :s, :t, :u, :v, :w, :x, :y, :z)
Benchmark.ips do |x|
x.report('hash') do
hash = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15, p: 16, q: 17, r: 18, s: 19, t: 20, u: 21, v: 22, w: 23, x: 24, y: 25, z: 26 }
hash[:a]; hash[:b]; hash[:c]; hash[:d]; hash[:e]; hash[:f]; hash[:g]; hash[:h]; hash[:i]; hash[:j]; hash[:k]; hash[:l]; hash[:m]; hash[:n]; hash[:o]; hash[:p]; hash[:q]; hash[:r]; hash[:s]; hash[:t]; hash[:u]; hash[:v]; hash[:w]; hash[:x]; hash[:y]; hash[:z]
end
x.report('struct') do
struct = MyStruct.new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26)
struct.a;struct.b;struct.c;struct.d;struct.e;struct.f;struct.g;struct.h;struct.i;struct.j;struct.k;struct.l;struct.m;struct.n;struct.o;struct.p;struct.q;struct.r;struct.s;struct.t;struct.u;struct.v;struct.w;struct.x;struct.y;struct.z
end
x.report('ostruct') do
ostruct = OpenStruct.new( a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15, p: 16, q: 17, r: 18, s: 19, t: 20, u: 21, v: 22, w: 23, x: 24, y: 25, z: 26)
ostruct.a;ostruct.b;ostruct.c;ostruct.d;ostruct.e;ostruct.f;ostruct.g;ostruct.h;ostruct.i;ostruct.j;ostruct.k;ostruct.l;ostruct.m;ostruct.n;ostruct.o;ostruct.p;ostruct.q;ostruct.r;ostruct.s;ostruct.t;ostruct.u;ostruct.v;ostruct.w;ostruct.x;ostruct.y;ostruct.z;
end
x.compare!
end
Warming up --------------------------------------
hash 51.741k i/100ms
struct 62.346k i/100ms
ostruct 1.010k i/100ms
Calculating -------------------------------------
hash 603.104k (± 3.9%) i/s - 3.053M in 5.070565s
struct 780.005k (± 3.4%) i/s - 3.928M in 5.041571s
ostruct 11.321k (± 3.4%) i/s - 56.560k in 5.001660s
Comparison:
struct: 780004.8 i/s
hash: 603103.8 i/s - 1.29x slower
ostruct: 11321.2 i/s - 68.90x slower
您可以看到struct快一点,但是它需要在使用它之前定义struct字段,因此,如果使用struct确实对性能至关重要,那么
答案 5 :(得分:1)
我在 Ruby 3.0.0p0(2020-12-25 修订版 95aff21468)[x86_64-darwin20] 上重新运行了 @mpospelov 的 benchmarks。
简单
Warming up --------------------------------------
hash 1.008M i/100ms
struct 423.906k i/100ms
ostruct 16.384k i/100ms
Calculating -------------------------------------
hash 9.923M (± 1.9%) i/s - 50.412M in 5.082029s
struct 4.327M (± 2.6%) i/s - 22.043M in 5.097358s
ostruct 158.771k (± 6.7%) i/s - 802.816k in 5.084066s
Comparison:
hash: 9923144.6 i/s
struct: 4327316.1 i/s - 2.29x (± 0.00) slower
ostruct: 158771.4 i/s - 62.50x (± 0.00) slower
庞大的名单
Warming up --------------------------------------
hash 71.378k i/100ms
struct 99.245k i/100ms
ostruct 855.000 i/100ms
Calculating -------------------------------------
hash 712.113k (± 4.9%) i/s - 3.569M in 5.024094s
struct 1.098M (± 2.9%) i/s - 5.558M in 5.066160s
ostruct 8.629k (± 4.8%) i/s - 43.605k in 5.066147s
Comparison:
struct: 1098071.6 i/s
hash: 712112.5 i/s - 1.54x (± 0.00) slower
ostruct: 8628.8 i/s - 127.26x (± 0.00) slower
结论