Почему символы в Ruby не рассматриваются как типы переменных?

Новичок в программировании и в Ruby, и я надеюсь, что этот вопрос о символах находится в линии. Я понимаю, что символы в Ruby (например, :book, :price) особенно полезны в качестве хеш-ключей и для общего, легкого, специфического подмножества того, что могут делать строки.

Однако меня смущают символы в одном отношении. В частности, когда они используются в attr_accessor типы методов, кажется, что они ведут себя больше как переменная. Например, attr_reader :book, :price,

Если это правда, что они являются переменными в этом использовании, это немного озадачивает, потому что они обычно не перечислены среди типов переменных (таких как $global, @instance, local, @@class и иногда, CONSTANT, типы переменных), когда типы переменных описаны.

И если символы являются переменными при использовании таким образом, какую область следует ожидать от них? Или они все еще как-то легкие строки в этом контексте? (Или, возможно, в более широком смысле, все ли символы, строки и переменные имеют фундаментальную утилитарную природу?) Заранее благодарим вас за ваши идеи и советы.

8 ответов

Решение

Символы, используемые в методах доступа, не являются переменными. Они просто представляют имя переменной. Переменные содержат некоторую ссылку, поэтому вы не можете использовать саму переменную при определении методов доступа. Например, предположим, что вы хотите определить метод доступа для переменной @foo в контексте, где его значение "bar", Что бы произошло, если бы синтаксис Ruby был таким:

attr_accessor @foo

Это ничем не отличается от написания:

attr_accessor "bar"

где у вас нет доступа к имени @foo что вас интересует. Следовательно, такие конструкции должны быть разработаны для обращения к именам переменных на мета-уровне. Символ используется по этой причине. Сами они не переменные. Они представляют имя переменной.

А переменные, относящиеся к методам доступа, являются переменными экземпляра.

Символы - это не переменные, а тип буквального значения, например, цифры и строки в кавычках. Важно отметить, что символы используются для представления переменных и других именованных значений во время выполнения Ruby. Поэтому, когда интерпретатор Ruby видит имя foo используется в качестве имени переменной или метода, то, что он ищет в хэше значений времени выполнения, является символом :fooне строка "foo", Фактически это было первоначальное использование термина "символ" в терминологии языка программирования; говорят, что переменные, функции, константы, методы и т. д. хранятся в "таблице символов" компилятора или интерпретатора.

Практически каждый раз, когда вы передаете название чего-либо в Ruby, вы собираетесь использовать символ. Если вы используете method_missing как универсальное средство для реализации произвольных методов в вашем объектном классе, символ - это то, что он получает в качестве аргумента, сообщающего ему имя метода, который был фактически вызван. Если вы осмотрите объект с .methods или же .instance_variables, что вы получите обратно, это массив символов. И так далее.

Они не являются переменными, потому что они не содержат значений, они неизменны. Дело в самой ценности. Это похоже на цифры. Вы не можете установить значение 1, 1 = 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

(Ответ на ваш комментарий)

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 << dog или же def dog.barkRuby создает анонимный класс, класс поля instance dog теперь указывает на этот анонимный класс, а оттуда на String. Методы, определенные в этом контексте с помощью def или define_method, попадают в таблицу методов анонимного класса.

В Ruby 1.9.2 введен объект #singleton_class. [Кирка] Возвращает одноэлементный класс obj, создавая его при необходимости. (Я добавляю) Это эквивалентно class << self; self end,

Язык программирования Ruby (O'Reiily) просто говорит: чтобы открыть собственный класс [singleton class] объекта o, используйте класс << o.

Так что я не знаю, как читать это вслух. Я читал, что некоторые предпочли бы o >> class, Только недавно я понял, как понять, что означает это странное выражение. Я произношу: переходите от o к анонимному классу.

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

То же самое верно для класса. С class MyClassMyClass, как экземпляр класса, является объектом с указателем на его класс Class. С def MyClass.some_method или же class << MyClassRuby создает анонимный класс, который вставляется между MyClass и Class, и в него входят методы класса.

Может быть что-то вроде: "из класса, создать экземпляр объекта-одиночки

Да для "от класса / объекта" до анонимного одноэлементного класса / собственного класса / метакласса. Но мы не воплощаем себя в жизнь. Self (в Smaltalk, это в C++/Java) является своего рода зарезервированным словом, которое обозначает получателя сообщения. dog.bark ЗЫ: на ОО языке мы говорим, что сообщение лает в отправленной объекту собаке. Внутри метода barkСобственная личность будет настроена на собаку, чтобы мы могли ссылаться на собаку. Это более очевидно с

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

some_method должен иметь возможность ссылаться на получателя в общем виде, будь то o1 или o2, для этого и предназначен self.

Чтобы ответить на ваши вопросы "Если это правда, что они являются переменными" и "область видимости", было бы проще ответить, что символы доступа не имеют ничего общего с переменными экземпляра, даже если это звучит иконоборчески. Они не указывают на переменные экземпляра. Средства доступа определяют только методы получения и установки. В Object # instance_variables указатель (*) говорит: Обратите внимание, что простое определение средства доступа не создает соответствующую переменную экземпляра.

В 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 = является синтаксическим сахаром для использования сеттера в качестве обычного метода с параметром: obj.name=('xyz')
4) метод получения name отвечает на значение @name
5) метод получения book отвечает nil, потому что переменная экземпляра @book не существует. Определение метода доступа attr_reader :book не определил соответствующую переменную экземпляра
6) метод получения book отвечает на значение, назначенное в initializeназывается new в строке 21. Переменная экземпляра @book была создана @book = p_book
строка 26) Я всегда считал, что методы доступа принимают только символы. Я обнаружил, что переменная возможна, но представляет ограниченный интерес.
7) метод сеттера title= создал @title. Это также показывает, что переменные экземпляра принадлежат одному объекту. Мы часто считаем, что они принадлежат всем экземплярам класса, как и в других языках. В этом случае @name принадлежит только объекту, созданному в строке 6.
8) метод получения 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

Руби attr_accessor, attr_reader, а также attr_writer это просто краткие способы избежать написания небольшого количества повторяющегося кода. Следующий вопрос раскрывает, как они работают: зачем использовать Ruby's attr_accessor, attr_reader и attr_writer?

Вместо того, чтобы думать о attr_reader :book как переменную, просто думайте об этом как об имени атрибута, который указан с помощью символа.

Круто, я думаю, вы уже поняли их. Однако почему они так важны?

Символы в Ruby неизменны, а строки изменяемы. Вы думаете, хорошо, круто, и что?

Предположим, у вас есть массив строк, например:

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

Для каждой новой строки, которую вы создаете, ruby ​​собирается создать строку / объект, который содержит значение "a", а поскольку строки являются изменяемыми, ruby ​​назначает разные идентификаторы для каждого из них. Если бы вы использовали вместо этого символы:

[ :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

Я надеюсь, что это поможет вам всем.

Другие вопросы по тегам