11

编程和 Ruby 新手,我希望这个关于符号的问题符合要求。我知道 Ruby 中的符号(例如 , :book:price特别用作哈希键,并且可以全方位地做一个轻量级的、特定的字符串可以做的事情的子集。

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

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

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

4

8 回答 8

7

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

几乎任何时候你在 Ruby 中传递一些东西的名字,你都会使用一个符号。如果您使用method_missing包罗万象的方式在您的对象类上实现任意方法,则符号是它作为参数接收的,告诉它实际调用的方法的名称。如果你用.methodsor检查一个对象.instance_variables,你得到的是一个符号数组。等等。

于 2012-12-13T00:22:01.090 回答
6

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

于 2012-12-13T00:21:45.227 回答
3

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

attr_accessor @foo

这与写作没有什么不同:

attr_accessor "bar"

您无法访问@foo您感兴趣的名称。因此,必须将此类构造设计为在元级别引用变量名称。符号就是因为这个原因。它们本身不是变量。它们代表变量的名称。

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

于 2012-12-13T01:14:34.707 回答
2

attr_accessor这些都是属于类Class的所有方法。他们期望符号作为参数。如果需要,您可以编写自己的attr_使用字符串版本。它只是一个红宝石成语。这是一个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
于 2012-12-13T00:25:52.943 回答
1

Ruby 的attr_accessor,attr_readerattr_writer只是避免编写一些重复代码的简写方式。下面的问题扩展了它们是如何工作的:为什么使用 Ruby 的 attr_accessor、attr_reader 和 attr_writer?

与其将其attr_reader :book视为变量,不如将其视为使用符号指定的属性的名称。

于 2012-12-13T00:21:15.047 回答
1

为了解决您的“如果它们是变量是真的”和“范围”问题,回答访问器符号与实例变量无关会更简单,即使这听起来是反传统的。它们不指向实例变量。访问器只定义 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 回答 nil :它不存在
3) 赋值已经创建了实例变量。
请注意,这name =是将 setter 用作带有参数的普通方法的语法糖:obj.name=('xyz')
4) getter 方法name回答 @name 的值
5) getter 方法book回答 nil 因为实例变量 @book 不存在。定义访问attr_reader :book器尚未定义相应的实例变量
6) getter 方法回答在 21 中调用的book赋值。实例变量 @book 已由initializenew@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

于 2012-12-13T13:46:01.903 回答
1

(回答您的评论)

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

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

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

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

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

Ruby 编程语言 (O'Reily) 简单地说:要打开对象 o 的 eigenclass [单例类],请使用 class << o。

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

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

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

可能是这样的:“从类中,实例化单例对象自身

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

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

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

于 2012-12-14T02:12:56.583 回答
0

很酷,我想你现在已经理解它们了。然而,为什么它们如此重要?

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 解释器中的名称。

这个视频真的帮助了我: Ruby's Symbols Explained

希望对大家有帮助。

于 2013-09-04T00:30:22.047 回答