我想知道是否有办法在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"
我需要帮助填写空白,以便调用显示将返回我想要的内容。
答案 0 :(得分:2)
这就是为什么这不可能并且没有意义。
想象一下,我添加到您的代码中:
n2_playlist = alternative
n2_playlist.display
n2_playlist
会显示什么? n2_playlist
? alternative
?我有两个引用指向同一个对象。它就像一个具有相同编程指令的遥控器。
为了让事情变得更有趣,我们假设:
[n2_playlist, alternative].each do |some_arg|
some_arg.display
end
它应该显示什么? some_arg
? n2_playlist
? alternative
? Source
或者这个:
Playlist.new.display #=> What should it print ? We have no variable pointing to it
使用Playlist.new
,您即可创建新对象,而alternative
和n2_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
这经历了许多非常规手段来完成你所询问的任务。现在可能你不知道命名播放列表类的东西的标准方式,这没关系。一开始没有人知道,这是一个学到的特质。
这是命名播放列表类的首选方法。
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
现在你有一个播放列表列表!甜!
当您进入元编程时,您可以使用非常规方法中的许多功能。元编程是代码编写代码的地方。这很有趣!