从类中调用实例的变量名

时间:2014-10-03 18:37:45

标签: ruby

我想知道是否有办法在Ruby中的类本身内调用类的实例设置的变量名。我想做以下几点。

class Playlist
  def display
    puts "the name of this playlist is ___"
  end
end

alternative = Playlist.new 
alternative.display
# => "the name of this playlist is alternative"

我需要帮助填写空白,以便调用显示将返回我想要的内容。

2 个答案:

答案 0 :(得分:2)

这就是为什么这不可能并且没有意义。

想象一下,我添加到您的代码中:

n2_playlist = alternative
n2_playlist.display

n2_playlist会显示什么? n2_playlistalternative?我有两个引用指向同一个对象。它就像一个具有相同编程指令的遥控器。

为了让事情变得更有趣,我们假设:

[n2_playlist, alternative].each do |some_arg|
  some_arg.display
end

它应该显示什么? some_argn2_playlistalternativeSource

或者这个:

Playlist.new.display #=> What should it print ? We have no variable pointing to it

使用Playlist.new,您即可创建新对象,而alternativen2_playlist只是对该对象的引用。据我所知,Ruby对象不知道(并且不关心)指向它们的引用。在Ruby中,您只能操纵对象。变量不是对象。 Source

最好将该类型放在initialize方法中。这样,您就可以拥有一个可以操作的实际String对象。

答案 1 :(得分:2)

非常规方法(解决您提出的问题)

好。为了后人,我会把代码放在如何完成你的要求上。请记住,这不是语言的用法。但是如果你进入元编程,这将是非常有用的知识。

class Playlist

  def display
    puts "Your playlist name is #{name}"
  end

  private
  def name
    scope = ObjectSpace.each_object(Binding).to_a[-1]
    scope.
      local_variables.
      select {|i| eval(i.to_s, scope) == self}.
      map(&:to_s).delete_if {|i| i== "_"}.first
  end

end

alternative = Playlist.new
# => #<Playlist:0x00000002caad08> 
alternative.display
# Your playlist name is alternative

详细信息(工作原理)

好吧,让我解释一下这些部分。 ObjectSpace 是存储所有对象的地方。您可以通过调用 ObjectSpace.count_objects 来查看存在多少个对象。在我看来,最有用的功能是 each_object 方法。使用此方法,您可以迭代许多已创建的特定对象。因此,对于播放列表,您可以调用 ObjectSpace.each_object(播放列表),然后获得一个Enumerable对象。我们可以通过在末尾附加 .to_a 将其转换为列表。但此时您正在使用这样的数组获取播放列表的实例:[#<Playlist:0x0000000926e540>, #<Playlist:0x000000092f4410>, #<Playlist:0x000000092f7d90>]。如果您想单独访问它们并执行某些操作,则此功能非常有用。但是这不是你想要做的,因为我们没有这些实例被赋值的实例化变量名。

我们真正想要的是 local_variables 方法,我们希望在主范围内调用它(而不是在类范围内)。如果您从显示方法中调用 local_variables ,则会返回一个空数组[]。但是如果你在创建实例后在主控制台中调用它,你会得到类似这样的[:alternative, :_]。现在我们正在谈论!现在存在从类外部获取范围以在其中使用的问题。追踪这很棘手。通常,您可以将绑定作为参数传递,或者甚至使用 TOPLEVEL_BINDING 。但我注意到的一些事情告诉我,这些都会创建一个 Binding 的实例,不再会更新。这意味着,一旦您拨打 TOPLEVEL_BINDING ,您定义的任何其他内容(如其他播放列表)都不会更新,并且会在 TOPLEVEL_BINDING.local_variables 列表中更新。这对我来说是一件令人伤心的事情。但我发现了解决这个问题的方法。

通过调用 ObjectSpace.each_object(Binding).to_a ,我们现在拥有每个绑定实例的列表。所以我们只需要知道如何获得最新的最新版本。经过实验,我发现最后一个将永远是最新的。所以我们按[-1]索引。现在我们可以在其上调用 .local_variables ,我们将始终在全局范围内获取最新的实例变量集合。这很棒!现在我们只需要将实例变量与我们所在的当前播放列表进行匹配。因此,我们从全局local_variables中选择与当前实例匹配的任何内容。我们需要调用eval来获取实例,并且使用eval我们需要告诉它运行什么范围,因此我们使用select {|i| eval(i.to_s, scope) == self}。从那里我们获取符号并使用 .map(&amp;:to_s)将它们映射到字符串,最后我们在列表中有一个我们不需要的额外项目。下划线符号是一种Ruby技巧,用于获取最后处理的内容。所以我们需要删除它,因为它评估的值与当前变量实例的id相同。所以我们做 .delete_if {| i |我==“_”} 。最后它是一个项目列表,我们想要的东西,所以我们用 .first

来选择它

注意:此范围选择方法在Rails中不起作用。有许多绑定实例化。最后一个和最大的local_variables不是最新的。

这经历了许多非常规手段来完成你所询问的任务。现在可能你不知道命名播放列表类的东西的标准方式,这没关系。一开始没有人知道,这是一个学到的特质。

命名播放列表的常用方法

这是命名播放列表类的首选方法。

class Playlist

  def initialize(name)
    @name = name
  end

  def display
    puts "Your playlist name is #{@name}"
  end

end

list = Playlist.new("Alternative")
list.display
# => "Your playlist name is Alternative"

这是相当直接的。最好使用语言的设计方式。

如果我是你,我会列出一个播放列表项目数组并像这样使用它。

list = []
list << Playlist.new("Alternative")
list << Playlist.new("Rock")
list
# => [#<Playlist:0x000000028a4f60 @name="Alternative">, #<Playlist:0x000000028e4868 @name="Rock">]
list[0].display
# Your playlist name is Alternative
list[1].display
# Your playlist name is Rock

现在你有一个播放列表列表!甜!

当您进入元编程时,您可以使用非常规方法中的许多功能。元编程是代码编写代码的地方。这很有趣!