代码之家  ›  专栏  ›  技术社区  ›  Walt Jones

什么时候在Ruby中使用结构比使用哈希更好?

  •  47
  • Walt Jones  · 技术社区  · 17 年前

    Ruby结构允许使用一组访问器生成实例:

    # Create a structure named by its constant
    Customer = Struct.new(:name, :address)     #=> Customer
    Customer.new("Dave", "123 Main")           #=> #<Customer name="Dave", address="123 Main">
    

    Customer = {:name => "Dave", :address => "123 Main"}
    

    在现实世界中,我应该选择什么样的结构(以及为什么),在选择其中一种结构时有哪些注意事项或陷阱?

    5 回复  |  直到 17 年前
        1
  •  22
  •   gaqzi    17 年前

    就我个人而言,当我想让一段数据像一个数据集合一样工作,而不是在一个框架下松散耦合时,我会使用struct Hash .

    
    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?

    我看到的另一个例子是 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 只响应您设置的属性, 响应任何属性集-但没有值集的将返回 Nil

        2
  •  10
  •   JDonner    16 年前

    结构具有通过索引和名称在其元素中获取的功能:

    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

    这有时很有用。

        3
  •  9
  •   mpospelov    8 年前

    对于那些喜欢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
    

    结论

        4
  •  8
  •   Community Mohan Dere    9 年前

    主要是表演。Struct的速度要快得多,按数量级排列。与哈希或OpenStruct相比,它消耗的内存更少。更多信息请点击此处: When should I use Struct vs. OpenStruct?

        5
  •  6
  •   the Tin Man    9 年前

    关于使用Hash的速度,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
    

    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相比,它过去比Struct慢:

    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
    
        6
  •  1
  •   ogirginc    5 年前

    benchmarks 关于Ruby 3.0.0p0(2020-12-25修订版95aff21468)[x86_64-darwin20]。

    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
    

    结论

    • 如果性能确实重要,请使用struct。
    推荐文章