Why are symbols in Ruby not thought of as a type of variable?

前端 未结 8 1523
臣服心动
臣服心动 2020-11-30 12:50

New to programming and to Ruby, and I hope this question about symbols is in line. I understand that symbols in Ruby (e.g., :book, :price) are usef

8条回答
  •  攒了一身酷
    2020-11-30 13:25

    To address your "If true that they are variables" and "scope" questions, it would have been simpler to answer that accessor symbols have nothing to do with instance variables, even if it sounds iconoclastic. They don't point to instance variables. Accessors only define getter and setter methods. Under Object#instance_variables, the Pickaxe(*) says : Note that simply defining an accessor does not create the corresponding instance variable.

    In Ruby, a variable does not exist until you assign a value to it. The following code demonstrates this.

    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
    

    Output :

    $ 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) empty array : accessors have not defined instance variables
    2) asking for instance variable @name answers nil : it does not exist
    3) assigning a value has created the instance variable.
    Note that name = is a syntactic sugar for using the setter as an ordinary method with a parameter : obj.name=('xyz')
    4) the getter method name answers the value of @name
    5) the getter method book answers nil because the instance variable @book does not exist. Defining an accessor attr_reader :book has not defined the corresponding instance variable
    6) the getter method book answers the value assigned in initialize, called by new on line 21. The instance variable @book has been created by @book = p_book
    line 26) I have always believed that accessors accept only symbols. I discover that a variable is possible, but of limited interest.
    7) the setter method title= has created @title. This also shows that instance variables belong to a single object. We often believe that they belong to all instances of the class, as in other languages. In this case, @name belongs only to the object created on line 6.
    8) the getter method title answers the value of @title

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

    Output :

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

    9) of course there is a tight correlation between an accessor symbol and the corresponding instance variable, because, behind the scene, Ruby creates methods which reference an instance variable of the same name. You could define your own getter and cheat.


    Note that besides class variables (@@var, some dislike them as ugly as global variables), classes can also have instance variables. I call them class instance variables :).
    class MyClass : Ruby allocates a new instance of class Class, defines a constant MyClass, and assigns the new instance to that constant. Thus MyClass is an ordinary object (instance of Class) and as such can have instance variables.

    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  
    

    Output :

    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"  
    

    More on singleton methods here : What does def `self.function` name mean?

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

提交回复
热议问题