为什么Ruby中的符号不​​被认为是一种变量?

编程和Ruby的新手,我希望这个关于符号的问题符合要求。 我理解Ruby中的符号(例如:book:price )特别适合作为散列键,并且可以全面地完成字符串可以执行的轻量级特定子集。

但是,我在某方面对符号感到困惑。 具体来说,当它们在attr_accessor类型的方法中使用时,它们看起来更像是一个变量。 例如, attr_reader :book, :price

如果它们是该用法中的变量,那么这有点令人费解,因为它们通常不在变量类型中列出(如$ global,@ instance,local,@@ class,有时候,CONSTANT,变量类型)被描述。

如果符号是以这种方式使用的变量,那么应该对它们有什么范围? 或者它们在这种情况下仍然是某种轻量级的字符串? (或者也许以更广泛的方式,符号,字符串和变量都具有基本的鸭子性质?)提前感谢您的见解和建议。

存取方法中使用的符号不是变量。 它们只是表示变量的名称。 变量包含一些引用,因此您无法在定义访问器方法时使用变量本身。 例如,假设您想要在其值为"bar"的上下文中为变量@foo定义一个访问器方法。 如果Ruby的语法是这样的话会发生什么:

 attr_accessor @foo 

这与写作没有什么不同:

 attr_accessor "bar" 

你无权访问你感兴趣的@foo名称。因此,这种结构必须设计成在元级别引用变量名。 符号用于此原因。 它们本身不是变量。 它们代表变量的名称。

与访问器方法相关的变量是实例变量。

符号不是变量,而是一种文字值,如数字和带引号的字符串。 值得注意的是,符号用于表示 Ruby运行时中的变量和其他命名值。 因此,当Ruby解释器将名称foo用作变量或方法名称时,它在Hash of runtime值中查找的是符号:foo ,而不是字符串"foo" 。 事实上,这是编程语言术语中“符号”一词的最初用法; 变量,函数,常量,方法等据说存储在编译器或解释器的“符号表”中。

几乎每当你在Ruby中传递某些东西的名字时,你都会使用一个符号。 如果使用method_missing作为catch-all来实现对象类的任意方法,则符号就是它作为参数接收的符号,告诉它实际调用的方法的名称。 如果使用.methods.instance_variables检查对象, .methods返回的是符号数组。 等等。

它们不是变量,因为它们不包含值,它们是不可变的。 事情就是价值本身。 它与数字相似。 您无法设置值11 = 2不起作用。

attr_accessor等属于类Class的所有方法。 他们希望符号作为参数。 如果需要,您可以编写自己的使用字符串的attr_版本。 它只是一个ruby成语。 这是attr_acessor的一个例子,它存储了我为家庭作业分配的attr_accessor所有先前值。

 class Class def attr_accessor_with_history(attr_name) attr_name = attr_name.to_s # make sure it's a string attr_reader attr_name # create the attribute's getter attr_reader attr_name+"_history" # create bar_history getter class_eval %Q" def #{attr_name}=(value) if !defined? @#{attr_name}_history @#{attr_name}_history = [nil] end @#{attr_name} = value @#{attr_name}_history << value end " end end 

Ruby的attr_accessorattr_readerattr_writer只是避免编写一些重复代码的attr_writer方法。 以下问题扩展了这些如何工作: 为什么要使用Ruby的attr_accessor,attr_reader和attr_writer?

而不是将attr_reader :book视为变量 ,只需将其视为使用符号指定的属性的名称

为了解决你的“如果它们是变量”和“范围”问题,可以更简单地回答,访问者符号与实例变量无关,即使它听起来是反传统的。 它们不指向实例变量。 访问器仅定义getter和setter 方法 。 在Object#instance_variables下,Pickaxe(*)表示:注意,只是定义一个访问器不会创建相应的实例变量。

在Ruby中,在为其赋值之前,变量不存在。 以下代码演示了这一点。

 class MyClass attr_accessor :name attr_reader :book end obj = MyClass.new # line 6 print '1) obj.instance_variables : '; p obj.instance_variables print '2) obj.name : '; p obj.name obj.name = 'xyz' print '3) obj.instance_variables : '; p obj.instance_variables print '4) obj.name : '; p obj.name print '5) obj.book : '; p obj.book class MyClass def initialize(p_book) @book = p_book end end obj = MyClass.new('The Pickaxe') # line 21 print '6) [new] obj.book : '; p obj.book class MyClass method_name = 'title' attr_accessor method_name # line 26 end obj.title = 'Programming Ruby' print '7) obj.instance_variables : '; p obj.instance_variables print '8) obj.title : '; p obj.title 

输出:

 $ ruby -w t.rb 1) obj.instance_variables : [] 2) obj.name : nil 3) obj.instance_variables : ["@name"] 4) obj.name : "xyz" 5) obj.book : nil 6) [new] obj.book : "The Pickaxe" 7) obj.instance_variables : ["@title", "@book"] 8) obj.title : "Programming Ruby" 

1)空数组:访问器没有定义实例变量
2)要求实例变量@name answers nil:它不存在
3)赋值已创建实例变量。
请注意, name =是一个语法糖,用于将setter用作带参数的普通方法: obj.name=('xyz')
4)getter方法name回答@name的值
5)getter方法book答案为零,因为实例变量@book不存在。 定义访问器attr_reader :book尚未定义相应的实例变量
6)getter方法book回答initialize分配的值,由第21行的new调用。实例变量@book由@book = p_book创建
第26行)我一直认为访问者只接受符号。 我发现变量是可能的,但兴趣有限。
7)setter方法title=创建了@title。 这也表明实例变量属于单个对象。 我们经常认为它们属于该类的所有实例,就像在其他语言中一样。 在这种情况下,@ name仅属于在第6行创建的对象。
8)getter方法title回答@title的值

 class MyClass def title # line 34 @book + ' (cheating !)' end end print '9) obj.title : '; p obj.title 

输出:

 t.rb:34: warning: method redefined; discarding old title 9) obj.title : "The Pickaxe (cheating !)" 

9)当然,存取符号和相应的实例变量之间存在紧密的相关性,因为在场景后面,Ruby创建了引用同名实例变量的方法。 你可以定义自己的吸气剂和作弊。

请注意,除了类变量(@@ var,有些人不喜欢它们像全局变量一样丑陋),类也可以有实例变量。 我称它们为类实例变量:)。
class MyClass :Ruby分配一个类Class的新实例,定义一个常量MyClass,并将新实例分配给该常量。 因此,MyClass是一个普通的对象(Class的实例),因此可以有实例变量。

 if RUBY_VERSION[0..2] == '1.8' class Object def singleton_class class << self self end end end end class MyClass singleton_class.instance_eval do attr_accessor :counter end @counter = 0 def initialize(p_book) @book = p_book self.class.counter += 1 end end print '10) MyClass.singleton_methods : '; p MyClass.singleton_methods print '11) MyClass.instance_variables : '; p MyClass.instance_variables obj = MyClass.new('Ruby') print '12) [new] obj.book ', MyClass.counter, ': '; p obj.book obj = MyClass.new('Metaprogramming') print '13) [new] obj.book ', MyClass.counter, ': '; p obj.book 

输出:

 t.rb:55: warning: method redefined; discarding old initialize 10) MyClass.singleton_methods : ["counter", "counter="] 11) MyClass.instance_variables : ["@counter"] 12) [new] obj.book 1: "Ruby" 13) [new] obj.book 2: "Metaprogramming" 

关于单例方法的更多信息: def`self.function`这个名字是什么意思?

(*) http://pragprog.com/book/ruby3/programming-ruby-1-9

(回答你的评论)

dog = 'dog'String.new("dog")

在dog = String.new之后,实例dog的字段类指向类String。

 class << dog puts "inside #{self}" #=> inside #> def bark puts 'woof' end end dog.bark #=> "woof" p dog.singleton_methods #=> ["bark"] 

使用class << dogdef dog.bark ,Ruby创建一个匿名类,实例dog的字段类现在指向此匿名类,并从那里指向String。 在此上下文中使用def或define_method定义的方法将进入匿名类的方法表。

Ruby 1.9.2引入了Object#singleton_class。 [Pickaxe]返回obj的单例类,必要时创建一个。 (我补充)它相当于class << self; self end class << self; self end

Ruby编程语言(O'Reiily)简单地说:打开对象o的本征类[singleton class],使用类<< o。

所以我不知道如何大声朗读它。 我读过有些人更喜欢o >> class 。 直到最近我才发现如何弄清楚这个奇怪的表达意味着什么。 我发音:从o到它的匿名类。

 class << MyClass def dog puts 'dog as class method' end end MyClass.dog #=> dog as class method 

一个class级也是如此。 对于class MyClass ,MyClass作为Class的实例,是一个带有指向其类Class的指针的对象。 使用def MyClass.some_methodclass << MyClass ,Ruby创建了一个在MyClass和Class之间插入的匿名类,并且类方法会进入它。

也许类似于:“从类中,实例化单例对象自我

是的,从“类/对象”到匿名单例类/特征类/元类。 但我们并没有实例化自我。 Self(在Smaltalk中,这在C ++ / Java中)是一种保留字,用于指定消息的接收者。 dog.bark :在OO语言中我们说消息吠叫发送给对象狗。 在方法bark内部,自我将被设置为狗,以便我们可以参考狗。 这更明显

 o1 = MyClass.new; o2 = MyClass.new o1.some_method; o2.some_method 

some_method必须能够以通用的方式引用接收器,是o1还是o2,这就是self的用途。

很酷,我猜你现在明白了。 但是为什么它们如此重要?

Ruby中的符号是不可变的,而字符串是可变的。 你觉得好酷,那又怎样?

假设你有一个字符串数组,如下所示:

  [ "a", "b", "a", "b", "a", "b", "c" ] 

对于创建ruby的每个新字符串,将创建一个字符串/对象,其中包含“a”的值,并且因为字符串是可变的,所以ruby会为每个字符串分配不同的id。 如果您要使用符号:

 [ :a, :b, :a, :b, :a, :b, :c ] 

Ruby现在将指向这些符号,它只会创建一次。

我们来做一些基准测试:

 require 'benchmark' Benchmark.bm do |x| x.report("Symbols") do a = :a 1000_000.times do b = :a end end x.report("Strings") do a = "a" 1000_000.times do b = "a" end end end ruby -w symbols.rb Symbols 0.220000 0.000000 0.220000 ( 0.215795) Strings 0.460000 0.000000 0.460000 ( 0.452653) 

如果你想看到你已经创建的所有符号,你可以这样做:

 Symbol.all_symbols 

您也可以向他们发送询问他们身份的消息:

 :a.object_id #=> 123 :a.object_id #=> 123 "a".id #=> 23323232 "a".id #=> some_blob_number 

再说那是因为Ruby中的字符串是可变的而符号则不是。 Ruby符号表示Ruby解释器中的名称。

这段video真的帮助了我: Ruby的符号解释

我希望它能帮到你们所有人。