gpt4 book ai didi

ruby - 如何将变量的名称标识为self的字符串?

转载 作者:数据小太阳 更新时间:2023-10-29 08:46:59 26 4
gpt4 key购买 nike

我为ruby openstruct库编写了一个包装器。使用此包装,用户声明一个变量,如下所示:

example = RStruct.new()

我想这样访问变量的名称:
example.name # => "example"

当从类内操作并调用 self.name时,这将被证明是有用的。
我构建了 RStruct类,因此添加name方法并不困难。我想从 self中找到用户的变量。
如果有语言能做到的话,那就是ruby,对吧?

最佳答案

这种情况在ruby中经常出现,以至于我为它写了一个gem。你在香草红宝石里通常会做的是:

fred = Foobar.new( name: 'fred' )

不仅“fred”在上面的行中重复两次,而且还必须满足命名参数 :fred,并且必须执行诸如编写 RStruct#name方法等杂务。如果您 gem install y_support,则可以编写:
require 'y_support/name_magic'

class Foobar
include NameMagic
end

现在:
Fred = Foobar.new
Fred.name #=> "Fred"

您只能将常量赋值(以大写字母开头)用于命名类包含 NameMagic的无名对象,但无论如何“fred”应该大写。ruby常量作为事物的大写专有名称加倍,ruby模块有时也被称为名称空间。这与通过常量赋值(如 Animal = Class.new,也称为ruby内置常量magic)来命名类的行为是一致的。
附录更详细地描述了名称魔术功能:
NameMagic还提供有限数量的与命名相关的常用服务。通常需要的是实例注册表:
Foobar.instances
#=> [Fred]

注册表本身是一个散列,可以通过以下方式访问:
Foobar.__instances__ # Don't modify this hash unless you know what you are doing.

一种比看起来更有用的方法是 #instance方法:
Foobar.instance "Fred"
#=> Fred
Foobar.instance Fred
#=> Fred

此方法将其参数(不管是字符串、对象本身还是其他什么)转换为相应的对象,并在没有此类实例时引发 NameError
Array是用 #names方法扩展的,因此您可以很容易地获得数组中对象的名称(我向自己确认这是一个经常需要的特性):
Foobar.instances.names
#=> ["Fred"]

Hash是用 #keys_to_names方法扩展的,因此您可以编写 { Fred => 42 }.keys_to_names来从使用对象切换到使用它们的名称。这个并不经常需要,但我确实包含了它,因为命名与散列密切相关,名称是一种散列函数,应该对人类有意义,并且很容易被人类记住。核心类的扩展到此为止。您可以在文档中查找的其他方法包括 Foobar.nameless_instancesFoobar.forgetFoobar.forget_nameless_instancesFoobar.forget_all_instances(以允许对废弃对象进行gced,这只是为了高级使用,并减少有关gem耗尽内存的假设投诉)。
当您两次分配一个常量时,gem的当前版本将严重中断,该常量已在下一个计划版本中修复,该版本的发布受到依赖项(我的另一个gem)的阻碍。另外,还感谢您指出了一种方法来抑制当您接触到deep名称空间中的一些模糊常量时生成的烦人警告。
gem还允许使用一个更标准的方法命名,使用 :name参数而不是常量赋值,因此这是gem用户不必做的另一项杂务:
Foobar.new name: "Joe"
Foobar.instances #=> [Fred, Joe]

或使用 #name=setter。
x = Foobar.new
x.name = "Sam"
Foobar.instances #=> [Fred, Joe, Sam]

我想在这里介绍的最后一个有用的特性是两个钩子, #name_set_hook#name_get_hook。第一个(如果对验证和/或修改建议的名称非常有用)。另外,很有可能您需要执行一些代码,而不是在实例化时执行(因为使用 name_magic的实例天生就没有名字),而是只在名称已知时执行,例如将名称下的对象注册到您的各种集合中。 #name_set_hook是输入代码的地方。这个功能有点不稳定,因为你必须知道规则:
块应该是三元的,具有顺序参数名称、实例和以前的名称。
块必须返回一个有效的名称,将使用该名称而不是建议的名称。这给了一个修改名称的机会,但是如果您不想修改它,您仍然必须返回未修改的版本。
块代码必须避免直接或间接地触发到目前为止未命名实例的名称查询,否则将导致无休止的循环。尤其是,不能为无名实例调用 #inspect#to_s方法。在别的地方做是可以的,但在这个街区不行。
Foobar.name_set_hook { |name, instance, old_name|
puts "Name #{name} was requsted for object #{instance.object_id}." # not "#{instance}",
# because "#{instance}" would invoke instance.to_s method.
puts "Such name is not acceptable to our church."
modified_name = "#{name}x"
puts "The name will be #{modified_name}."
# notify your tables that the object has just been named with modified_name
acceptable_name # the block must return the name to be used, even if it doesn't modify it
}

Nathan = Foobar.new
#=> Name Nathan was requsted for object 76778240.
#=> Such name is not acceptable to our church.
#=> The name will be Nathanx.

Foobar.instances #=> [Fred, Joe, Sam, Nathanx]

更没用的钩子是 #name_get_hook。只有当你想让名字有一种以上的表达方式,并且你想选择一种特定的方式时,它才有用。例如,物理单位可以保持为所有caps常量,例如
class Unit; include NameMagic end
GRAM, METRE, SECOND = Unit.new, Unit.new, Unit.new
#=> [GRAM, METRE, SECOND]

但你可能希望他们用小写字母来表达自己:
Unit.name_get_hook { |name| name.downcase }
Unit.instances
#=> [gram, metre, second]

方法 #instance识别两种情况:
Unit.instance "metre"
#=> metre
Unit.instance "METRE"
#=> metre

但在内部,存储的名称仍然是大写版本,我们可以通过调查通过 #__instances__方法访问的实例注册表散列来确认。
Unit.__instances__[ Unit.instance( "metre" ) ]
#=> "METRE"

这是我的实际用例,激发了我添加 #name_get_hook特性的灵感,这是必需的,因为名称必须以大写字母开头,但单位的惯例是用所有小写字母书写。换言之,使用 #name_get_hook的潜规则是,必须有一种明确的方式从所呈现的替代形式派生实际名称。
gem的github回购协议是 https://github.com/boris-s/y_support。现在我应该振作起来,把这个文本放到自述文件中。

关于ruby - 如何将变量的名称标识为self的字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24924340/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com