什么时候在Ruby中使用Struct而不是Hash更好?
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">
这看起来方便而且function强大,但是,一个Hash可以做些非常相似的事情:
Customer = {:name => "Dave", :address => "123 Main"}
什么是现实世界的情况下,我应该更喜欢一个结构(以及为什么),以及在select一个之前有什么警告或陷阱?
就我个人而言,当我想使一块数据像一个数据集合而不是在一个Hash
下松散耦合时,我使用了一个结构。
例如我做了一个脚本,从Youtube下载video,在那里我有一个结构来表示一个video,并testing是否所有的数据都在位:
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
后来在我的代码中,我有一个循环遍历video源HTML页面中的所有行,直到empty?
不会返回true。
我见过的另一个例子是James Edward Gray IIs configuration类 ,它使用OpenStruct
轻松添加从外部文件加载的configurationvariables:
#!/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
一个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
这有时是有用的。
主要是performance。 按大小顺序,结构要快得多。 与Hash或OpenStruct相比,消耗更less的内存。 更多信息: 什么时候应该使用Struct与OpenStruct?
关于使用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
如果结构,哈希或者结构或者OpenStruct被定义了一次然后被使用了很多次,那么访问速度变得更加重要,并且结构开始发光:
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的速度大大提高, 它以前比基于我以前见过的基准testing结果要慢,并且比较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