Почему символы в 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.bark
Ruby создает анонимный класс, класс поля 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 MyClass
MyClass, как экземпляр класса, является объектом с указателем на его класс Class. С def MyClass.some_method
или же class << MyClass
Ruby создает анонимный класс, который вставляется между 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`?
Руби 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
Я надеюсь, что это поможет вам всем.