替换Ruby中的to_s方法。不打印所需的字符串

时间:2022-09-25 18:26:34

So, I am just beginning to learn Ruby and I included a to_s method in my Class so that I can simply pass the Object to a puts method and have it return more than just the Object ID. I made a mistake and defined it as such:

因此,我刚刚开始学习Ruby,我在我的类中包含了to_s方法,这样我就可以简单地将对象传递给put方法,并让它返回的对象不只是对象ID。

def to_s
    puts "I'm #{@name} with a health of #{@health}."
end

instead of:

而不是:

def to_s
    "I'm #{@name} with a health of #{@health}."
end

So, when I do this while using the first code block:

当我使用第一个代码块时

player1 = Player.new("larry")
puts player1

I get an object ID and a string when I execute the above two lines of code and not just the string. Why is this? I get this output:

当我执行上面两行代码而不只是字符串时,就会得到一个对象ID和一个字符串。这是为什么呢?我得到这个输出:

I'm Larry with a health of 90.
#<Player:0x007fca1c08b270>

I am trying to think about why the first version of the program doesn't just print out the string to console, but instead returns the object ID and the string. I thought that when I pass the object to puts, all that is happening is that puts turns around and calls the to_s method to get the player's string representation. Right?

我正在思考为什么程序的第一个版本不只是打印到控制台,而是返回对象ID和字符串。我认为,当我传递对象的时候,所有正在发生的事情是,put转身并调用to_s方法来获得播放器的字符串表示。对吧?

4 个解决方案

#1


5  

When given arguments that are not strings or arrays puts calls rb_obj_as_string to turn its arguments into strings (see rb_io_puts)

当给定非字符串或数组的参数时,调用rb_obj_as_string将其参数转换为字符串(参见rb_io_put)

If you search for rb_obj_as_string through the ruby codebase (I find http://rxr.whitequark.org useful for this) you can see it's defined as

如果您通过ruby代码库搜索rb_obj_as_string(我发现http://rxr.whitequark.org对此很有用),您可以看到它被定义为

VALUE rb_obj_as_string(VALUE obj)
{
  VALUE str;

  if (RB_TYPE_P(obj, T_STRING)) {
    return obj;
  }
  str = rb_funcall(obj, id_to_s, 0);
  if (!RB_TYPE_P(str, T_STRING))
    return rb_any_to_s(obj);
  if (OBJ_TAINTED(obj)) OBJ_TAINT(str);
  return str;
}

In brief this:

简而言之:

  • returns straightaway if the argument is already a string
  • 如果参数已经是字符串,则立即返回
  • calls to_s
  • 调用to_s
  • if the result is not a string, call rb_any_to_s and return that.
  • 如果结果不是字符串,则调用rb_any_to_s并返回该字符串。

rb_any_to_s is what implements the default "class name and id" result that you're seeing: for any object it returns a string of the form #<ClassName: 0x1234567890abcdef>

rb_any_to_s实现了您所看到的默认“类名和id”结果:对于任何对象,它都返回一个形式为# 的字符串

Returning to your code, when you run puts player1 it calls rb_obj_as_string to convert your player to a string.

返回到您的代码,当您运行put player1时,它调用rb_obj_as_string来将您的播放器转换为字符串。

This first calls your to_s method, which uses puts to output your message. Your method then returns nil (because that's what puts always returns) so ruby calls rb_any_to_s, and that is what the outermost puts ends up using.

这首先调用您的to_s方法,该方法使用put来输出消息。然后,您的方法返回nil(因为这是放置总是返回的值),因此ruby调用rb_any_to_s,这就是最外层放置最后使用的值。

#2


2  

Experiential Rule: If the result of to_s is not a String, then ruby returns the default.

体验规则:如果to_s的结果不是字符串,则ruby返回默认值。

Application of Rule: puts() returns nil, which means your to_s method returns nil, and nil is not a String, so ruby returns the default.

Application of Rule: put()返回nil,这意味着您的to_s方法返回nil, nil不是字符串,所以ruby返回默认值。

Another example:

另一个例子:

class Object
  def inspect
    'obj-inspect'
  end

  def to_s
    'obj-to_s'
  end
end

class Dog 
  def inspect
    'dog-inspect'
  end

  def to_s
    nil
  end
end

puts Dog.new

--output:--
#<Dog:0x1001b6218>

Once to_s fails to return a String, ruby does not continue along the method lookup path to call another to_s method. That makes some sense: the method was found, so there is no need to look up the method in a parent class. Nor does ruby alternatively call inspect() to get a result.

一旦to_s未能返回一个字符串,ruby就不会沿着方法查找路径继续调用另一个to_s方法。这是有道理的:找到了该方法,所以不需要在父类中查找该方法。ruby也不会调用检查()来获得结果。

Where does the default come from? I think ruby must directly call the Object#to_s method which is a method written in C--thereby bypassing ruby's method overriding mechanism.

违约从何而来?我认为ruby必须直接调用对象#to_s方法,这是用C语言编写的方法——从而绕过ruby的方法重写机制。

#3


1  

That's because the puts returns nil, so does that version of to_s:

这是因为put返回nil,所以to_s的版本是:

def to_s
  puts "I'm #{@name} with a health of #{@health}."
end

With puts player1, player1.to_s method is called, which prints the String "I'm ...", but the return value is that of the puts call inside to_s, which is nil.

把player1 player1。调用to_s方法,输出字符串“I'm…”,但返回值是to_s中的put调用的值,即nil。

So player1 is an object of which to_s returns nil, thus puts player1 in the end prints the result of the inherited to_s method.

player1是一个对象,to_s返回nil,因此在末尾将player1打印继承的to_s方法的结果。

#4


0  

The first example using puts will write to stdout and return nil. It does not actually return a String.

第一个使用put的示例将写入stdout并返回nil。它实际上并没有返回一个字符串。

The second example returns a String.

第二个示例返回一个字符串。

If you want to write to the console you can, but you will need to also return the value.

如果想要写入控制台,可以这样做,但是还需要返回值。

#or put it in a variable first and return that after you print it
def to_s
  puts "I'm #{@name} with a health of #{@health}."
  "I'm #{@name} with a health of #{@health}."
end

#1


5  

When given arguments that are not strings or arrays puts calls rb_obj_as_string to turn its arguments into strings (see rb_io_puts)

当给定非字符串或数组的参数时,调用rb_obj_as_string将其参数转换为字符串(参见rb_io_put)

If you search for rb_obj_as_string through the ruby codebase (I find http://rxr.whitequark.org useful for this) you can see it's defined as

如果您通过ruby代码库搜索rb_obj_as_string(我发现http://rxr.whitequark.org对此很有用),您可以看到它被定义为

VALUE rb_obj_as_string(VALUE obj)
{
  VALUE str;

  if (RB_TYPE_P(obj, T_STRING)) {
    return obj;
  }
  str = rb_funcall(obj, id_to_s, 0);
  if (!RB_TYPE_P(str, T_STRING))
    return rb_any_to_s(obj);
  if (OBJ_TAINTED(obj)) OBJ_TAINT(str);
  return str;
}

In brief this:

简而言之:

  • returns straightaway if the argument is already a string
  • 如果参数已经是字符串,则立即返回
  • calls to_s
  • 调用to_s
  • if the result is not a string, call rb_any_to_s and return that.
  • 如果结果不是字符串,则调用rb_any_to_s并返回该字符串。

rb_any_to_s is what implements the default "class name and id" result that you're seeing: for any object it returns a string of the form #<ClassName: 0x1234567890abcdef>

rb_any_to_s实现了您所看到的默认“类名和id”结果:对于任何对象,它都返回一个形式为# 的字符串

Returning to your code, when you run puts player1 it calls rb_obj_as_string to convert your player to a string.

返回到您的代码,当您运行put player1时,它调用rb_obj_as_string来将您的播放器转换为字符串。

This first calls your to_s method, which uses puts to output your message. Your method then returns nil (because that's what puts always returns) so ruby calls rb_any_to_s, and that is what the outermost puts ends up using.

这首先调用您的to_s方法,该方法使用put来输出消息。然后,您的方法返回nil(因为这是放置总是返回的值),因此ruby调用rb_any_to_s,这就是最外层放置最后使用的值。

#2


2  

Experiential Rule: If the result of to_s is not a String, then ruby returns the default.

体验规则:如果to_s的结果不是字符串,则ruby返回默认值。

Application of Rule: puts() returns nil, which means your to_s method returns nil, and nil is not a String, so ruby returns the default.

Application of Rule: put()返回nil,这意味着您的to_s方法返回nil, nil不是字符串,所以ruby返回默认值。

Another example:

另一个例子:

class Object
  def inspect
    'obj-inspect'
  end

  def to_s
    'obj-to_s'
  end
end

class Dog 
  def inspect
    'dog-inspect'
  end

  def to_s
    nil
  end
end

puts Dog.new

--output:--
#<Dog:0x1001b6218>

Once to_s fails to return a String, ruby does not continue along the method lookup path to call another to_s method. That makes some sense: the method was found, so there is no need to look up the method in a parent class. Nor does ruby alternatively call inspect() to get a result.

一旦to_s未能返回一个字符串,ruby就不会沿着方法查找路径继续调用另一个to_s方法。这是有道理的:找到了该方法,所以不需要在父类中查找该方法。ruby也不会调用检查()来获得结果。

Where does the default come from? I think ruby must directly call the Object#to_s method which is a method written in C--thereby bypassing ruby's method overriding mechanism.

违约从何而来?我认为ruby必须直接调用对象#to_s方法,这是用C语言编写的方法——从而绕过ruby的方法重写机制。

#3


1  

That's because the puts returns nil, so does that version of to_s:

这是因为put返回nil,所以to_s的版本是:

def to_s
  puts "I'm #{@name} with a health of #{@health}."
end

With puts player1, player1.to_s method is called, which prints the String "I'm ...", but the return value is that of the puts call inside to_s, which is nil.

把player1 player1。调用to_s方法,输出字符串“I'm…”,但返回值是to_s中的put调用的值,即nil。

So player1 is an object of which to_s returns nil, thus puts player1 in the end prints the result of the inherited to_s method.

player1是一个对象,to_s返回nil,因此在末尾将player1打印继承的to_s方法的结果。

#4


0  

The first example using puts will write to stdout and return nil. It does not actually return a String.

第一个使用put的示例将写入stdout并返回nil。它实际上并没有返回一个字符串。

The second example returns a String.

第二个示例返回一个字符串。

If you want to write to the console you can, but you will need to also return the value.

如果想要写入控制台,可以这样做,但是还需要返回值。

#or put it in a variable first and return that after you print it
def to_s
  puts "I'm #{@name} with a health of #{@health}."
  "I'm #{@name} with a health of #{@health}."
end