为什么我不能在Integer类中覆盖self?

时间:2022-10-24 21:05:10

I want to be able to write number.incr, like so:

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

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

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

The error I'm seeing states: Can't change the value of self

我看到的错误说明:无法改变自我的价值

If that's true, how do bang! methods work?

如果这是真的,怎么轰!方法有效吗?

2 个解决方案

#1


2  

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).

对象是类指针和一组实例方法(请注意,此链接是Ruby的旧版本,因为它非常简单,因此更好地用于解释目的)。

"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).

“指向”一个对象意味着你有一个变量,它将对象的位置存储在内存中。然后要对对象做任何事情,首先要转到内存中的位置(我们可能会说“跟随指针”)来获取对象,然后执行操作(例如调用方法,设置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.

所有Ruby代码都在某个对象的上下文中执行。这是保存实例变量的地方,这是Ruby查找没有接收器的方法(例如$ stdout是$ stdout.puts中的接收器“hi”,当前对象是puts中的接收器“hi” )。有时您需要对当前对象执行某些操作。处理对象的方法是通过变量,但是哪个变量指向当前对象?没有一个。为了满足此需求,提供了关键字self。

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.

self就像一个变量,因为它指向当前对象的位置。但它不像变量,因为你不能为它赋予新的价值。如果可以的话,那个点之后的代码会突然在另一个对象上运行,这会让人感到困惑并且没有使用变量的好处。

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).

还要记住,对象由存储内存地址的变量跟踪。什么是自我= 2应该是什么意思?它只是意味着当前代码的运行方式就像它被调用2一样吗?或者它是否意味着指向旧对象的所有变量现在都将其值更新为指向新值?这不是很清楚,但前者不必要地引入了身份危机,而后者非常昂贵,并且引入了不清楚什么是正确的情况(我将在下面进一步讨论)。

You cannot mutate Fixnums

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

有些对象在Ruby中的C级是特殊的(false,true,nil,fixnums和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.

指向它们的变量实际上并不存储内存位置。相反,地址本身存储对象的类型和标识。无论何处重要,Ruby都会检查它是否是一个特殊对象(例如,在查找实例变量时),然后从中提取值。

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.

因此,在存储器中没有存储对象123的位置。这意味着self包含Fixnum 123的想法,而不是像往常一样的内存地址。与变量一样,它将在必要时进行特殊检查和处理。

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).

因此,你不能改变对象本身(虽然看起来它们保留了一个特殊的全局变量,允许你在像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).

他们为什么要这样做呢?为了提高性能,我假设。存储在寄存器中的数字只是一系列位(通常为32位或64位),这意味着存在用于加法和乘法之类的硬件指令。也就是说,ALU被连线以在单个时钟周期内执行这些操作,而不是用软件编写算法,这将花费更多的数量级。通过像这样存储它们,它们避免了在内存中存储和查看对象的成本,并且它们获得了可以使用硬件直接添加两个指针的优点。但是请注意,Ruby中还有一些额外的成本(C中没有)(例如检查溢出并将结果转换为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

  • Make a wrapper object: I'm not going to advocate this one, but it's the closest to what you're trying to do. Basically create your own class, which is mutable, and then make it look like an integer. There's a great blog post walking through this at http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html it will get you 95% of the way there
  • 制作一个包装器对象:我不打算提倡这个,但它最接近你想做的事情。基本上创建自己的类,它是可变的,然后使它看起来像一个整数。有一篇很棒的博客文章,请访问http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html,它会在95%的路上
  • Don't depend directly on the value of a Fixnum: I can't give better advice than this without knowing what you're trying to do / why you feel this is a need.
  • 不要直接依赖于Fixnum的价值:我不能在不知道你想要做什么/为什么你觉得这是一个需要的情况下提供比这更好的建议。

Also, you should show your code when you ask questions like this. I misunderstood how you were approaching it for a long time.

此外,当您提出这样的问题时,您应该显示您的代码。我误解了你是如何接近它的。

#2


1  

It's simply impossible to change self to another object. self is the receiver of the message send. There can be only one.

将自己改变为另一个对象根本不可能。 self是消息发送的接收者。只可以有一个人。

If that's true, how do bang! methods work?

如果这是真的,怎么轰!方法有效吗?

The bang (!) is simply part of the method name. It has absolutely no special meaning whatsoever. It is a convention among Ruby programmers to name surprising variants of less surprising methods with a bang, but that's just that: a convention.

爆炸(!)只是方法名称的一部分。它绝对没有任何特殊意义。 Ruby程序员之间的惯例是用一声巨响命名令人惊讶的不太令人惊讶的方法变体,但这就是:一个惯例。

#1


2  

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).

对象是类指针和一组实例方法(请注意,此链接是Ruby的旧版本,因为它非常简单,因此更好地用于解释目的)。

"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).

“指向”一个对象意味着你有一个变量,它将对象的位置存储在内存中。然后要对对象做任何事情,首先要转到内存中的位置(我们可能会说“跟随指针”)来获取对象,然后执行操作(例如调用方法,设置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.

所有Ruby代码都在某个对象的上下文中执行。这是保存实例变量的地方,这是Ruby查找没有接收器的方法(例如$ stdout是$ stdout.puts中的接收器“hi”,当前对象是puts中的接收器“hi” )。有时您需要对当前对象执行某些操作。处理对象的方法是通过变量,但是哪个变量指向当前对象?没有一个。为了满足此需求,提供了关键字self。

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.

self就像一个变量,因为它指向当前对象的位置。但它不像变量,因为你不能为它赋予新的价值。如果可以的话,那个点之后的代码会突然在另一个对象上运行,这会让人感到困惑并且没有使用变量的好处。

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).

还要记住,对象由存储内存地址的变量跟踪。什么是自我= 2应该是什么意思?它只是意味着当前代码的运行方式就像它被调用2一样吗?或者它是否意味着指向旧对象的所有变量现在都将其值更新为指向新值?这不是很清楚,但前者不必要地引入了身份危机,而后者非常昂贵,并且引入了不清楚什么是正确的情况(我将在下面进一步讨论)。

You cannot mutate Fixnums

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

有些对象在Ruby中的C级是特殊的(false,true,nil,fixnums和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.

指向它们的变量实际上并不存储内存位置。相反,地址本身存储对象的类型和标识。无论何处重要,Ruby都会检查它是否是一个特殊对象(例如,在查找实例变量时),然后从中提取值。

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.

因此,在存储器中没有存储对象123的位置。这意味着self包含Fixnum 123的想法,而不是像往常一样的内存地址。与变量一样,它将在必要时进行特殊检查和处理。

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).

因此,你不能改变对象本身(虽然看起来它们保留了一个特殊的全局变量,允许你在像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).

他们为什么要这样做呢?为了提高性能,我假设。存储在寄存器中的数字只是一系列位(通常为32位或64位),这意味着存在用于加法和乘法之类的硬件指令。也就是说,ALU被连线以在单个时钟周期内执行这些操作,而不是用软件编写算法,这将花费更多的数量级。通过像这样存储它们,它们避免了在内存中存储和查看对象的成本,并且它们获得了可以使用硬件直接添加两个指针的优点。但是请注意,Ruby中还有一些额外的成本(C中没有)(例如检查溢出并将结果转换为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

  • Make a wrapper object: I'm not going to advocate this one, but it's the closest to what you're trying to do. Basically create your own class, which is mutable, and then make it look like an integer. There's a great blog post walking through this at http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html it will get you 95% of the way there
  • 制作一个包装器对象:我不打算提倡这个,但它最接近你想做的事情。基本上创建自己的类,它是可变的,然后使它看起来像一个整数。有一篇很棒的博客文章,请访问http://blog.rubybestpractices.com/posts/rklemme/019-Complete_Numeric_Class.html,它会在95%的路上
  • Don't depend directly on the value of a Fixnum: I can't give better advice than this without knowing what you're trying to do / why you feel this is a need.
  • 不要直接依赖于Fixnum的价值:我不能在不知道你想要做什么/为什么你觉得这是一个需要的情况下提供比这更好的建议。

Also, you should show your code when you ask questions like this. I misunderstood how you were approaching it for a long time.

此外,当您提出这样的问题时,您应该显示您的代码。我误解了你是如何接近它的。

#2


1  

It's simply impossible to change self to another object. self is the receiver of the message send. There can be only one.

将自己改变为另一个对象根本不可能。 self是消息发送的接收者。只可以有一个人。

If that's true, how do bang! methods work?

如果这是真的,怎么轰!方法有效吗?

The bang (!) is simply part of the method name. It has absolutely no special meaning whatsoever. It is a convention among Ruby programmers to name surprising variants of less surprising methods with a bang, but that's just that: a convention.

爆炸(!)只是方法名称的一部分。它绝对没有任何特殊意义。 Ruby程序员之间的惯例是用一声巨响命名令人惊讶的不太令人惊讶的方法变体,但这就是:一个惯例。