为什么我不能重写Integer类的自我?(Why can't I overwrite sel

2019-10-21 16:11发布

我希望能够写number.incr ,就像这样:

num = 1; num.incr; num #=> 2

我看到美国的错误: Can't change the value of self

如果这是真的,怎么办砰! 方法的工作?

Answer 1:

You cannot change the value of self

An object is a class pointer and a set of instance methods (note that this link is an old version of Ruby, because its dramatically simpler, and thus better for explanatory purposes).

"Pointing" at an object means you have a variable which stores the object's location in memory. Then to do anything with the object, you first go to the location in memory (we might say "follow the pointer") to get the object, and then do the thing (e.g. invoke a method, set an ivar).

All Ruby code everywhere is executing in the context of some object. This is where your instance variables get saved, it's where Ruby looks for methods that don't have a receiver (e.g. $stdout is the receiver in $stdout.puts "hi", and the current object is the receiver in puts "hi"). Sometimes you need to do something with the current object. The way to work with objects is through variables, but what variable points at the current object? There isn't one. To fill this need, the keyword self is provided.

self acts like a variable in that it points at the location of the current object. But it is not like a variable, because you can't assign it new value. If you could, the code after that point would suddenly be operating on a different object, which is confusing and has no benefits over just using a variable.

Also remember that the object is tracked by variables which store memory addresses. What is self = 2 supposed to mean? Does it only mean that the current code operates as if it were invoked 2? Or does it mean that all variables pointing at the old object now have their values updated to point at the new one? It isn't really clear, but the former unnecessarily introduces an identity crisis, and the latter is prohibitively expensive and introduce situations where it's unclear what is correct (I'll go into that a bit more below).

You cannot mutate Fixnums

Some objects are special at the C level in Ruby (false, true, nil, fixnums, and symbols).

Variables pointing at them don't actually store a memory location. Instead, the address itself stores the type and identity of the object. Wherever it matters, Ruby checks to see if it's a special object (e.g. when looking up an instance variable), and then extracts the value from it.

So there isn't a spot in memory where the object 123 is stored. Which means self contains the idea of Fixnum 123 rather than a memory address like usual. As with variables, it will get checked for and handled specially when necessary.

Because of this, you cannot mutate the object itself (though it appears they keep a special global variable to allow you to set instance variables on things like Symbols).

Why are they doing all of this? To improve performance, I assume. A number stored in a register is just a series of bits (typically 32 or 64), which means there are hardware instructions for things like addition and multiplication. That is to say the ALU, is wired to perform these operations in a single clock cycle, rather than writing the algorithms with software, which would take many orders of magnitude longer. By storing them like this, they avoid the cost of storing and looking the object in memory, and they gain the advantage that they can directly add the two pointers using hardware. Note, however, that there are still some additional costs in Ruby, that you don't have in C (e.g. checking for overflow and converting result to Bignum).

Bang methods

You can put a bang at the end of any method. It doesn't require the object to change, it's just that people usually try to warn you when you're doing something that could have unexpected side-effects.

class C
  def initialize(val)
    @val = val         # => 12
  end                  # => :initialize

  def bang_method!
    "My val is: #{@val}"  # => "My val is: 12"
  end                     # => :bang_method!
end                       # => :bang_method!

c = C.new 12    # => #<C:0x007fdac48a7428 @val=12>
c.bang_method!  # => "My val is: 12"
c               # => #<C:0x007fdac48a7428 @val=12>

Also, there are no bang methods on integers, It wouldn't fit with the paradigm

Fixnum.instance_methods.grep(/!$/)  # => [:!]

# Okay, there's one, but it's actually a boolean negation
1.!  # => false

# And it's not a Fixnum method, it's an inherited boolean operator
1.method(:!).owner  # => BasicObject

# In really, you call it this way, the interpreter translates it
!1  # => false

Alternatives

  • 做一个包装对象:我不会鼓吹这一个,但它是最接近你想要做什么。 基本上,创建自己的类,它是可变的,然后使它看起来像一个整数。 有一个伟大的博客文章通过这个在走http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html它可以把你的方式95%的有
  • 不要直接对一个Fixnum的值取决于:不知道你正在试图做的/你为什么觉得这是一个需要什么,我不能给比这更好的建议。

此外,你应该表现出你的代码时,你问这样的问题。 我误解你是如何接近了很长一段时间。



Answer 2:

这是根本不可能改变self到另一个对象。 self是发送消息的接收器。 只可以有一个人。

如果这是真的,怎么办砰! 方法的工作?

感叹号( ! )是方法名称的一部分。 它绝对没有任何特殊含义。 这是Ruby程序员之间的约定来命名的一声巨响那么令人惊讶的方法令人惊讶的变种,但是这仅仅是:一个约定。



文章来源: Why can't I overwrite self in the Integer class?