在Ruby中什么时候使用Struct而不是Hash?

5q4ezhmt  于 2023-05-17  发布在  Ruby
关注(0)|答案(5)|浏览(110)

我没有太多的编程经验。但是,对我来说,Struct似乎有点类似于Hash。

  • Struct能做什么?
  • 有什么是Struct能做而Hash不能做的吗?

在谷歌搜索之后,Struct的概念在C中很重要,但我对C了解不多。

kcugc4gi

kcugc4gi1#

结构与使用散列表的不同之处如下(除了代码的外观):

  • 一个结构有一组固定的属性,而你向哈希添加新的键。
  • 调用结构体示例上不存在的属性将导致NoMethodError,而从散列中获取不存在的键的值将仅返回nil。
  • 不同结构的两个示例永远不会相等,即使结构具有相同的属性,示例具有相同的值(即Struct.new(:x).new(42) == Struct.new(:x).new(42)为假,而Foo = Struct.new(:x); Foo.new(42)==Foo.new(42)为真)。
  • 结构体的to_a方法返回一个值数组,而hash上的to_a方法会得到一个键值对数组(其中“pair”表示“两个元素的数组”)。
  • 如果是Foo = Struct.new(:x, :y, :z),则可以执行Foo.new(1,2,3)来创建Foo的示例,而无需拼写属性名称。

所以要回答这个问题:如果要用一组已知的属性对对象进行建模,请使用结构。当你想对任意对象建模时,使用散列表(例如计算每个单词在字符串中出现的频率或将昵称Map到全名等。绝对不是结构体的工作,而用名字、年龄和地址建模一个人将是Person = Struct.new(name, age, address)的完美选择)。
作为旁注:C结构体与Ruby结构体几乎没有任何关系,所以不要让自己被这一点所迷惑。

s6fujrry

s6fujrry2#

我知道这个问题几乎得到了很好的回答,但令人惊讶的是,没有人谈论Struct的最大区别之一和真实的的好处。我想这就是为什么有人还在问。
我理解其中的区别,但是当哈希可以做同样的事情,并且更容易处理时,使用结构体比使用哈希有什么真实的的优势呢?看起来结构是多余的。
Struct更快

require 'benchmark'

Benchmark.bm 10 do |bench|
  bench.report "Hash: " do
    50_000_000.times do { name: "John Smith", age: 45 } end
  end

  bench.report "Struct: " do
    klass = Struct.new(:name, :age)
    50_000_000.times do klass.new("John Smith", 45) end
  end

end

# ruby 2.2.2p95 (2015-04-13 revision 50295) [x64-mingw32].
#                 user     system      total        real
# Hash:       22.340000   0.016000  22.356000 ( 24.260674)
# Struct:     12.979000   0.000000  12.979000 ( 14.095455)

# ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin11.0]
# 
#                  user     system      total        real
# Hash:       31.980000   0.060000  32.040000 ( 32.039914)
# Struct:     16.880000   0.010000  16.890000 ( 16.886061)
bgtovc5b

bgtovc5b3#

另一个主要区别是您可以向结构添加行为方法。

Customer = Struct.new(:name, :address) do
  def greeting
    "Hello #{name}!"
  end
end
Customer.new("Dave", "123 Main").greeting  # => "Hello Dave!"
hxzsmxv2

hxzsmxv24#

Struct文档:
Struct是一种使用访问器方法将多个属性捆绑在一起的方便方法,而无需编写显式类。
另一方面,Hash
Hash是键值对的集合。它类似于Array,除了索引是通过任何对象类型的任意键完成的,而不是整数索引。按键或值遍历散列的顺序可能看起来是任意的,通常不会按照插入顺序。
主要区别在于您如何访问数据。

ruby-1.9.1-p378 > Point = Struct.new(:x, :y)
 => Point 
ruby-1.9.1-p378 > p = Point.new(4,5)
 => #<struct Point x=4, y=5> 
ruby-1.9.1-p378 > p.x
 => 4 
ruby-1.9.1-p378 > p.y
 => 5 
ruby-1.9.1-p378 > p = {:x => 4, :y => 5}
 => {:x=>4, :y=>5} 
ruby-1.9.1-p378 > p.x
NoMethodError: undefined method `x' for {:x=>4, :y=>5}:Hash
    from (irb):7
    from /Users/mr/.rvm/rubies/ruby-1.9.1-p378/bin/irb:17:in `<main>'
ruby-1.9.1-p378 > p[:x]
 => 4 
ruby-1.9.1-p378 > p[:y]
 => 5

简而言之,当你想要一个"plain old data" structure的类时,你会创建一个新的Struct(可选地,意图用更多的方法扩展它),当你根本不需要正式类型时,你会使用Hash。

toiithl6

toiithl65#

如果你只是要封装数据,那么哈希(或哈希数组)就可以了。如果你打算让数据操作或与其他数据交互,那么Struct可以打开一些有趣的可能性:

Point = Struct.new(:x, :y)
point_a = Point.new(0,0)
point_b = Point.new(2,3)

class Point
  def distance_to another_point
    Math.sqrt((self.x - another_point.x)**2 + (self.y - another_point.y)**2)
  end
end

puts point_a.distance_to point_b

相关问题