Ruby对象与哈希表的区别

5
下面的代码片段返回一个对象。
class Person
  def initialize(name, gender)
    @name = name
    @gender = gender
  end
end
x = Person.new("Dan", "M")
=> #<Person:0x007f6f96600560 @name="Dan", @gender="M">
  • 对象< ... >和哈希{ ... }之间有什么区别?为什么Ruby类不直接返回哈希?
  • 对象中的0x007f6f96600560是什么?我很确定它不是object_id

3
0x00...(object_id << 1).to_s(16) 的结果,其中 object_id 左移一位后转化成 16 进制。关于面向对象编程中的对象有很多书籍已经写得很详细了。你不应该期望在 SO 上得到此类问题的答案,因为人们需要花费数年时间并阅读许多书籍才能理解。 - Aleksei Matiushkin
2
OP似乎在暗示所有的类都应该从Hash进行子类化,并将实例变量名和值存储为键值对。事实上,JavaScript的工作方式有些类似。 - sawa
4
简短回答:因为 Ruby 不是一种基于原型的语言。 - Eric Duminil
@EricDuminil 谢谢!简短明了。那么,这是否归结为设计选择呢?也就是说,原型设计通过重用现有的哈希表可以适用于 Ruby,就像 JavaScript 重用现有对象作为原型一样,但 Ruby 最终采用了基于类的设计? - user7184349
2个回答

7

对象 → 散列表

来自 Pat Shaughnessy 的优秀书籍 《深入 Ruby 内部》

每个 Ruby 对象都是类指针和实例变量数组的组合。

这里有一个稍微更长的描述:

用户定义的 Ruby 对象由一个称为 RObject 的结构体表示,并由称为 VALUE 的指针引用。

在 RObject 中,还有另一个称为 RBasic 的结构体,所有 Ruby 值都将具有该结构体。

除了 RBasic 结构体外,RObject 还包含 numiv,一个对象拥有的实例变量数量的计数器,ivptr,指向实例变量值数组的指针,以及 iv_index_tbl,它是指向存储在对象关联的 RClass 结构中的哈希表的指针,该哈希表将每个实例变量的名称/标识映射到其在 ivtpr 数组中的位置。

从任何 Ruby 对象中,都可以提取实例变量的散列表:

(参见此处)

class Object
  def instance_variables_hash
    Hash[instance_variables.map { |name| [name, instance_variable_get(name)] } ]
  end
end

以您的示例为例:

x.instance_variables_hash
=> {:@name=>"Dan", :@gender=>"M"}

哈希 → 对象?

但是从这个哈希中你无法重新创建出x对象,因为你缺少一个关键信息: x是哪个类的实例?

例如,你不知道可以发送哪些方法给x

class Dog
  def initialize(name, gender)
    @name = name
    @gender = gender
  end

  def bark
    puts "WOOF"
  end
end

person = Person.new("Dan", "M")
dog    = Dog.new("Dan", "M")

p person.instance_variables_hash
# {:@name=>"Dan", :@gender=>"M"}
p dog.instance_variables_hash == person.instance_variables_hash
# true
person.bark
# undefined method `bark' for #<Person:0x007fb3b20ed658 @name="Dan", @gender="M">

object_id

从检查字符串中获取 object_id:

"0x007f6f96600560".sub('0x','').to_i(16)/2
#=> 70058620486320

回到之前:

"0x" + (70058620486320 * 2).to_s(16).rjust(14,'0')
#=> "0x007f6f96600560"

3
当然,有时候你可以使用对象和哈希表来完成相同的事情。像这样在对象中存储键值对:
[3] pry(main)> class Person
  def initialize(name, gender)
    @name = name
    @gender = gender
  end
end

[3] pry(main)* => :initialize

[4] pry(main)> x = Person.new("Dan", "M")
=> #<Person:0x00000003708098 @gender="M", @name="Dan">


[13] pry(main)> y = Person.new("Peter", "M")
=> #<Person:0x0000000391fca0 @gender="M", @name="Peter">


[22] pry(main)> z = {name: "Maria", gender: "F"}
=> {:name=>"Maria", :gender=>"F"}

但是,从类/对象哈希的定义来看,这些对象并不能完全发挥面向对象编程语言的全部功能:

Ruby is a perfect Object Oriented Programming Language. The features of the object-oriented programming language include:

Data Encapsulation:

Data Abstraction:

Polymorphism:

Inheritance:

These features have been discussed in Object Oriented Ruby.

An object-oriented program involves classes and objects. A class is the blueprint from which individual objects are created. In object-oriented terms, we say that your bicycle is an instance of the class of objects known as bicycles.

Take the example of any vehicle. It comprises wheels, horsepower, and fuel or gas tank capacity. These characteristics form the data members of the class Vehicle. You can differentiate one vehicle from the other with the help of these characteristics.

A vehicle can also have certain functions, such as halting, driving, and speeding. Even these functions form the data members of the class Vehicle. You can, therefore, define a class as a combination of characteristics and functions.

还有一个哈希:

哈希是一组键值对,例如:"员工" => "薪资"。它类似于数组,但索引是通过任意对象类型的任意键来完成的,而不是整数索引。

因此,为了存储数据,我建议您使用哈希。

另一方面,正如评论中所示,对象表示中出现的数字是对象ID,但添加了一些操作:

1)按位左移:

5 << 1  # gives 10

2) 转换为十六进制

(10).to_s(16)

"a"

pry(main)> x = Person.new("Dan", "M")
=> #<Person:0x00000003708098 @gender="M", @name="Dan">
[5] pry(main)> x.object_id
=> 28852300
[8] pry(main)> (x.object_id << 1 ).to_s(16)
=> "3708098"

最后,在Ruby中,您可以通过以下方式获取对象的哈希表示:
x.instance_variables.each {|var| hash[var.to_s.delete("@")] = x.instance_variable_get(var) }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接