Понять, объявлена ли переменная
Найдите в интернете способ выяснить объявлена ли переменная, и напишите программу, которая пишет, объявлены ли переменные a и b .
Затем объявите переменную a и запустите программу.
Получится примерно так:
Переменная 'a' определена Переменная 'b' не определена
Выяснить, определена ли переменная можно методом defined?
if defined?(a) puts "Определена переменная a" end
Ruby: с нуля – часть 3: переменные, константы и литералы
Предыдущая часть – часть 2: классы и объекты.
В Ruby есть пять типов переменных, часть из которых мы кратко рассмотрели в предыдущей части.
Глобальные перменные Ruby
Глобальные переменные начинаются с символа $ . Непроинициализированные глобальные переменные имеют значение nil (null, void) и вызывают предупрежение при использовании опции -w .
Примечание: интересное обсуждение nil vs null в английском языке можно найти тут>>>.
Пример использования глобальных переменных в Ruby:
#!/usr/bin/ruby $global_variable = 10 class Class1 def print_global puts "Global variable in Class1 is #$global_variable" end end class Class2 def print_global puts "Global variable in Class2 is #$global_variable" end end class1obj = Class1.new class1obj.print_global class2obj = Class2.new class2obj.print_global
$ ./globals.rb Global variable in Class1 is 10 Global variable in Class2 is 10
Примечание: в Ruby вы можете получить доступ к значениею любой перменной с помощью символа # .
Переменные экземпляра
Переменные экземпляра начинаются с символа @ , например:
#!/usr/bin/ruby $glob = "global" class Customer def initialize(id, name, addr) @cust_id=id @cust_name=name @cust_addr=addr end def display_details() puts "Customer id #@cust_id" puts "Customer name #@cust_name" puts "Customer address #@cust_addr" puts "Global var: #$glob" end end # Create Objects cust1=Customer.new("1", "John", "Wisdom Apartments, Ludhiya") cust2=Customer.new("2", "Poul", "New Empire road, Khandala") # Call Methods cust1.display_details() cust2.display_details()
Тут $glob – глобальная переменная, а @cust_id , @cust_name и @cust_addr – переменные объекта (или инстанса класса).
Результат выполнения такого кода:
$ ./instance_var.rb Customer id 1 Customer name John Customer address Wisdom Apartments, Ludhiya Global var: global Customer id 2 Customer name Poul Customer address New Empire road, Khandala Global var: global
Переменные класса
Переменные класса начинаются с двух символов @ , т.е. – @@ и должны быть проинициализированные перед использованием в методах.
Ссылка на непроинициализированную переменную класса вызовет ошибку.
Переменные класса доступны всем потокам класса или модуля, в которых они заданы.
Переназначение данных в такой перменной вызовет предупреждение при использовании опции -w .
$ cat class_var.rb #!/usr/bin/ruby class Customer @@no_of_customers=0 def initialize(id, name, addr) @cust_id=id @cust_name=name @cust_addr=addr end def display_details() puts "Customer id #@cust_id" puts "Customer name #@cust_name" puts "Customer address #@cust_addr" end def total_no_of_customers() @@no_of_customers += 1 puts "Total number of customers: #@@no_of_customers" end end # Create Objects cust1=Customer.new("1", "John", "Wisdom Apartments, Ludhiya") cust2=Customer.new("2", "Poul", "New Empire road, Khandala") # Call Methods cust1.total_no_of_customers() cust2.total_no_of_customers()
Тут @@no_of_customers является переменной класса. Результат выполнения такого кода:
$ ./class_var.rb Total number of customers: 1 Total number of customers: 2
Локальные переменные
Локальные переменные начинаются со строчной буквы или символа подчеркивания “ _ “. Область видимости локальной переменной может ограничиваться классом, модулем, функцией, оператором end или парой фигурных скобок “ <> “.
Обращение к непроинициализированной переменной обрабатывается как ее объявление.
В примере выше локальными перменными являются id , name и addr :
. def initialize(id, name, addr) @cust_id=id @cust_name=name @cust_addr=addr end .
Константы в Ruby
Имена констант всегда обозначаются буквами в верхнем регистре.
Область видимости констант, заданных в классе или модуле огранивается этим классом или модулем, тогда как константы заданные вне класса – будут доступны глобально.
Определять константу можно вне методов класса. Обращение к непроинициализированной константе вызовет ошибку, а попытка изменить значение константы вызовет предупреждение.
Пример констант в Ruby:
#!/usr/bin/ruby class Example VAR1 = 100 VAR2 = 200 def show puts "Value of first Constant is #" puts "Value of second Constant is #" end end # Create Objects object=Example.new() object.show
$ ./const.rb Value of first Constant is 100 Value of second Constant is 200
Примечание: переменные и константы в Ruby неплохо описаны на русском тут>>>.
Псевдопеременные в Ruby
Псевдопеременные – это специальные перменные, которые схожи с локальными переменными – но ведут себя как константы. Вы не можете присвоить свое значение таким переменным:
- self : определяет объект, чей метод был вызван;
- true : представление True ;
- false :представление False ;
- nil : неопределенное значение;
- __FILE__ : имя текущего файла;
- __LINE__ : номер строки в текущем файле.
Базовые литералы в Ruby
1. Целочисленные литералы
Целые числа в Ruby могут иметь значение от -2 30 до 2 30-1 и от -2 62 до 2 62-1 . Такие числа хранятся в объектах класса Fixnum , а большие числа – Bignum .
Примечание: Fixnum vs Bignum могут быть сравнены с int и long int в Java или C. Подробнее – тут>>>.
Примеры записи целочисленных литералов:
123 # Fixnum decimal 1_234 # Fixnum decimal with underline -500 # Negative Fixnum 0377 # octal 0xff # hexadecimal 0b1011 # binary ?a # character code for 'a' ?n # code for a newline (0x0a) 12345678901234567890 # Bignum
2. Числа с плавающей точкой
Числа с плавающей точкой являются объектами класса Float и могут иметь такой вид:
123.4 # floating point value 1.0e6 # scientific notation 4E20 # dot not required 4e+20 # sign before exponential
3. Строковые литералы
Строки в Ruby – это последовательность 8-битных байтов, которые являются объектами класса String . Строки в двойных кавычках позволяют выполнять замену символов в строке и использование обратного слеша. Строки в одинарных кавычках не позволяют выполнять замену, а обратный слеш допустим только в виде “ \ ” или “ ‘ “.
$ cat string.rb #!/usr/bin/ruby -w puts 'escape using "\"'; puts 'That's right';
$ ruby string.rb escape using "" That's right
Вы можете превратить результат любого выражения в Ruby в строку с помощью последовательности # < выражение >, например:
#!/usr/bin/ruby -w puts "Multiplication Value : #";
$ ruby expr.rb Multiplication Value : 86400
4. Управляющие последовательности
Ниже приведен сисок управляющих последовательностей в Ruby:
Character represented
Ruby on Rails c нуля!
RubyDev Ruby Tutorial > О переменных и константах
сентября 1, 2012 | Published in Ruby, Основы | 6 Comments
Честно говоря я не очень понимаю в чем возникает путаница с переменными в Ruby, однако мне уже не раз задавали вопросы типа: Что такое @-переменная? Что такое @@-переменная? А в чем их разница?
В этой статье я постараюсь рассказать обо всех типах переменных и константах языка Ruby, а на подобные вопросы буду давать ссылку на эту статью.
В Ruby существует 4 вида переменных и константы.
Локальные переменные
product , users , file , user_rights — все это примеры имен локальных переменных. Локальные переменные замкнуты в той области видимости, где они объявлены: тело модуля, тело класса, тело метода, объект, блок кода. Снаружи той области где они объявлены эти переменные не видны, потому область объявления включая вложенные области является областью видимости локальных переменных. Пример:
def my_method message = "Bye!" lmbd = ->(message) < puts message >lmbd.call("Hello!") puts message end my_method # Hello! # Bye!
Как видите, в нашем методе имеется 2 переменных с именем message, первая создается в самом начале тела метода и хранит строку «Bye!», а вторая принадлежит лямбде и хранит переданное в вызов лямбды значение. Областью видимости первой является все тело метода, а областью видимости второй — объект типа Proc — лямбда. Именно по этой причине message не меняет свое значение при вызове лямбды, так как у лямбды создается своя собственная переменная с тем же именем.
Переменные экземпляра
Следующий тип переменных — это переменные экземпляра класса или переменные объекта (экземпляр класса и объект — два названия одного и того же). Имена этих переменных начинаются со знака @, например:
@user , @articles , @attributes_hash , и т.д.
Областью видимости этих переменных является объект. Пример:
class User def initialize(login, password, email) @login = login @password = password @email = email end def user_data @user_data ||= < login: @login, password: @password, email: @email >end end user = User.new "admin", "qwerty", "admin@admin.com" #=> # user.user_data #=> "admin", :password=>"qwerty", :email=>"admin@admin.com">
Не смотря на то, что переменные @login, @password и @email были объявлены внутри метода #initialize, одни доступны и в методе #user_data. Областью видимости переменных экземпляра класса является весь объект.
Список переменных объекта можно получить используя метод #instance_variables:
user.instance_variables #=> [:@login, :@password, :@email, :@user_data]
Получить значение переменной экземпляра класса можно используя метод #instance_variable_get :
user.instance_variable_get :@email #=> "admin@admin.com"
Объявить новую переменную экземпляра класса или переопределить существующую можно используя метод #instance_variable_set :
user.instance_variable_set :@email, "admin@rubydev.ru" #=> "admin@rubydev.ru"
user.instance_variable_set :@name, «Vladimir»
#=> «Vladimir»
user.instance_variables
#=> [:@login, :@password, :@email, :@user_data, :@name]
Проверить объявлена ли определенная переменная экземпляра можно используя метод #instance_variable_defined? :
user.instance_variable_defined? :@name #=> true user.instance_variable_defined? :@last_name #=> false
Стоит отметить, что классы будучи также объектами также имеют переменные экземпляра класса.
class User def initialize(login, password, email) @id = User.increment_users_count @login = login @password = password @email = email end def user_data @user_data ||= < id: @id, login: @login, password: @password, email: @email >end def users_count self.class.instance_variable_get :@users_count end def self.users_count @users_count ||= 0 end def self.users_count=(value) @users_count = value end def self.increment_users_count self.users_count += 1 end end user = User.new "Vladimir", "123456", "admin@rubydev.ru" #=> # User.users_count #=> 1 user2 = User.new "Vitaly", "qwerty", "vitaly@rubydev.ru" #=> # User.users_count #=> 2 User.instance_variables #=> [:@users_count]
Переменные объекта самого класса недоступны в его экземплярах, точнее вы не можете обращаться к ним напрямую, но можете использовать методы #instance_variable_get или #instance_variable_set, например так:
def users_count self.class.instance_variable_get :@users_count end
Объявив такой метод для экземпляра класса User, мы можем получать значение счетчика экземпляров следующим образом:
user.users_count #=> 2
Переменные класса
Теперь рассмотрим переменные класса. Переменные класса — это переменные, имена которых начинаются с @@, например:
@@users , @@table_name , @@sql_adapter , @@very_very_long_class_variable_name
Переменные классов отличаются от переменных экземпляра класса тем, что будучи объявлены в любом месте, их областью видимости будет класс и все его экземпляры. То есть в отличие от переменных объекта самого класса, к переменным класса можно обратиться напрямую. Для того, чтобы продемонстрировать это я перепишу код класса User, что мы использовали ранее, с применением переменных класса.
class User @@users_count = 0 def initialize(login, password, email) @id = @@users_count += 1 @login = login @password = password @email = email end def user_data @user_data ||= < id: @id, login: @login, password: @password, email: @email >end def users_count @@users_count end def self.users_count @@users_count end end user = User.new "Vladimir", "123456", "admin@rubydev.ru" #=> # User.users_count #=> 1 user.users_count #=> 1 User.class_variables #=> [:@@users_count] User.class_variable_get :@@users_count #=> 1 User.class_variable_set :@@users_count, 100 #=> 100 User.users_count #=> 100 user.users_count #=> 100 User.class_variable_defined? :@@users_count #=> true
В общем работа с переменными класса очень похожа на работу с переменными экземпляра класса. Основная рекомендация к использованию переменных класса такая: Используйте переменные класса, если объекты нуждаются в данных из класса или для синхронизации между экземплярами класса. Примером обмена данными между объектами служит используемый нами в примерах счетчик экземпляров класса User.
Глобальные переменные
Глобальные переменные — это переменные, имена которых начинаются с символа $, например:
$: , $LOAD_PATH , $binding и т.д.
Глобальные переменные так называются так, потому, что независимо от области в которой они объявлены, они видны везде.
Получить список глобальных переменных можно используя метод global_variables. Вызвав global_variables в IRB REPL я получил следующий результат:
global_variables #=> [:$;, :$-F, :$@, :$!, :$SAFE, :$~, :$&, :$`, :$', :$+, :$=, :$KCODE, :$-K, :$,, :$/, :$-0, :$\, :$_, :$stdin, :$stdout, :$stderr, :$>, :$Вам лучше избегать создания глобальных переменных и переопределения значения уже существующих. В Ruby глобальные переменные используются в служебных целях, все приведенные выше глобальные переменные являются предопределенными и хранят определенные данные важные для работы вашего приложения. С ними нужно работать осторожно. Список предопределенных глобальных переменных с описанием доступен здесь: http://ruby.runpaint.org/globals
Теперь подошло время разобраться с константами. Константы — это «переменные», чье значение не изменяется, а имя начинается с заглавной литеры, наприме:User , UsersController , ProductsHelper и т.д.
Имена классов и модулей являются константами.
Несмотря на то, что в других языках переопределить значение константы нельзя, в Ruby это возможно, однако вы получете предупреждение.
MyConst = "rubydev.ru" MyConst = ["Rubydev.ru"] #(irb):77: warning: already initialized constant MyConst MyConst #=> ["Rubydev.ru"]Что еще можно сказать о константах? — Немного о метамагии!
Немного метамагии работы с константамиЕсли вы совсем взрослы и даже умеете работать с Rails, то вы, должно быть, использовали код вроде такого:
belongs_to :author, class_name: "User"Как же по строке «User» удается понять, что подразумевается использование класса User? Я не смотрел в исходники и потому не могу достоверно утверждать, но я подозреваю, что там используется следующий прием:
Kernel.const_get "User" #=> UserТаким простым образом мы «преобразуем» строку в константу, а посколько имена классов — константы, то в такой образ наше приложение способно понять, что мы хотим сделать.
Responses
- says:
сентября 1, 2012 at 17:57 (#) Ну не Kernel.const_get тогда уж, а Object.const_get, и вообще несколько сложнее все, тк нужно учитывать неймспейсы.. В общем в рельсе у строк есть метод #constantize — там все это и происходит.- says:
сентября 1, 2012 at 18:32 (#) Есть неочевидная, но существенная разница между переменными объекта класса и переменными класса (кроме того, что к первым нельзя напрямую обратиться из экземпляра). При наследовании переменные объекта класса у каждого класса будут свои, тогда как переменные класса наследуются, и изменение такой переменной где-то в потомке повлияет и на родителя и на прочих потомков.- admin says:
сентября 1, 2012 at 21:36 (#) Спасибо, Иван, это момент я забыл осветить, в ближайшее время добавлю. Михаил, насчет неймспейсов — это да, но здесь самый простой пример для новичков. Если его понять, то и как в случае с вложениями быть будет понятно. Да, ActiveSupport содержит такой метод расширяющий String, но статья исключительно о Ruby. Сейчас вникаю в разницу между Kernel.const_get и Object.const_get.- Shogun says:
сентября 13, 2012 at 18:40 (#) А разве в руби не 6 переменных?
1. константа — TEST
2. локальная — test
3. глобальная — $test
class Tester
@test # 4. переменная экземпляра класса (!)
@@test # 5. переменная класса
def testing
print @test # 6. переменная экземпляра (!)
end
end В книге «Язык программирования Ruby» делается различие между 4 и 6.
Объясни пожалуйста этот момент.- admin says:
сентября 13, 2012 at 21:27 (#) Shogun, в Ruby класс — это объект. В первом случае @test будет переменной объекта Tester, а во втором переменной объекта Tester.new. В любом случае обе переменные являются переменными объекта или экземпляра или instance, но только объект/экземпляр/instance которомы они принадлежат — разные.- sooik says:
августа 16, 2013 at 11:18 (#) Не работает ссылка =(Ruby, SmallTalk и переменные класса
Статья, является переводом заметки Пата Шонаси (Pat Shaughnessy), в оригинале звучащей как Ruby, Smalltalk and Class Variables.
Пару недель назад статья Эрни Миллера (Ernie Miller) натолкнула меня на вопрос: а как работают переменные класса в Руби? После небольшого исследования, оказалось что переменные класса могут быть потенциальным источником проблем. Фактически, Джон Нунмейкер (John Nunemaker) уже написал статью «Переменные класса и экземпляра в Руби», которая датируется 2006 годом и остаётся актуальной и сейчас. Фундаментальная проблема переменных класса в том, что они разделяются между самим классом и всеми его подклассами – как Джон объяснял еще шесть лет назад, и это может вести к неразберихе и странному поведению кода.
Для меня же главный вопрос: “Почему?”. Почему Руби делит это значение между всеми подклассами? Почему есть различие между переменными класса и переменными экземпляра? Откуда эти идеи произростают? Оказывается, ответ прост: переменные класса работают также, как работали в значительно более древнем языке, называемом Smalltalk. Smalltalk был изобретен в начале 1970х извесным компьютерным ученым Аланом Кеем (Alan Kay) и группой коллег, работавших в лаборатории Xerox PARC. С изобретением Smalltalk, Аллан не просто изобрел язык программирования; он придумал всю концепцию Объектно Ориентированного Программирования (ООП) и впервые реализовал ее. Хотя и не широко распространен сейчас, Smalltalk повлиял на многие другие объектно-ориентированные языки, которые широко распространены сегодня — наиболее важными из которых являются Объектный C и Руби.
Сегодня я хочу взглянуть, как переменные класса работают в Smalltalk, и сравнить в противопоставлении, как они работают в Руби. Вы увидите, что идея переменных класса — не единственное заимствование из Smalltalk. Большая часть из объектной модели Руби также была взята из Smalltalk.
Переменные класса в Руби
Сперва, давайте глянем, что из себя представляют переменные класса, и как они работают в Руби, используя пример Джона Нунмейкера 2006 года: вот простой Руби класс, Polygon , который содержит единственную переменную класса @@sides .
class Polygon @@sides = 10 def self.sides @@sides end end puts Polygon.sides =>10Этот пример довольно простой: @@sides — это переменная, к которой имеет доступ любой класс или метод экземпляра класса Polygon . В примере, sides — метод класса, который ее возвращает.
На концептуальном уровне, внутри, Руби ассоциирует переменную @@sides с тем же участком памяти, что представляет и Polygon класс:Неприятности начинаются, когда вы определяете подкласс; еще один из примеров Джона:
class Triangle < Polygon @@sides = 3 end puts Triangle.sides =>3 puts Polygon.sides =>3Заметьте, что обе переменные класса, Triangle.sides и Polygon.sides , были изменены на 3. По сути, внутри себя Руби создает единственную переменную, которую разделяют оба класса:
Я могу написать более детально о внутренней реализации переменных класса в Руби в следующем посте моего блога, но пока я буду использовать простейшие диаграммы. Вместо этого, давайте переключимся и узнаем чуть больше о Smalltalk…
Что такое Smalltalk?
Как я сказал выше, Алан Кей изобрел Smalltalk одновременно с объектно-ориентированным программированием, когда работал в Xerox PARC в начале 1970х. Это та же лаборатория, что изобрела персональный компьютер, графический интерфейс пользователя, и Ethernet и много много других вещей. На этом фоне изобретение ООП кажется наименее важным из изобретений!
В Smalltalk, Кей предложил терминологию и идеи, разумеющиеся сами собой сегодня. Каждое значение в Smalltalk, включая языковые конструкции, такие как блоки, являются объектом. Программа на Smalltalk состоит из этих объектов и пути их взаимодействия; чтобы вызвать определенную функцию на Smalltalk, вы 'шлете сообщение' объекту, который реализует эту функцию. В Smalltalk функции называются 'методами'. Объект реализует серию методов. Все это должно звучать очень похоже, конечно же.
С самого начала, концепция ООП Кея включала идею объектов 'класс'. Объектные классы были описаны, как серия поведений (методов), каждый экземпляр которых мог быть вызван. Smalltalk также реализовал концепцию полиморфизма, которая позволяет разработчику определить ‘подклассы’, наследовать поведение своих ‘суперклассов’. Все эти понятия, которые мы часто используем сегодня, были придуманы Кеем и его коллегами 40 лет назад.
Smalltalk, однако, больше чем язык программирования — это целая графическая среда разработки. Я считаю Smalltalk предвестником Visual Studio и XCode, разработанным, когда Microsoft и Apple даже не существовало, в мире, где компьютеры использовались только в академических или правительственных целях. Еще одна впечатляющая цель Алана Кея и команды Smalltalk, поставленная изначально, была в том, чтобы использовать их визуальное окружение для обучения детей в школах. Это по-настоящему удивительная история!
Чтобы узнать больше о истории и зарождении Smalltalk, я настоятельно рекомендую к прочтению ‘Раннюю историю Smalltalk’ (html, или pdf, или pdf но без диаграмм), ретроспективный обзор Кей написал позднее в 1990х. Это увлекательный рассказ о том, как Кей и его коллеги заимствовали идеи даже из более раннего прошлого, но их комбинация была тяжелой работой, креативной, где чистым талантом удалось сделать огромный шаг вперед и совершить революцию в компьютерном научном мире их дней, а так же и наших.
Первую рабочую версию Smalltalk Алан Кей создал в 1972 году – по его собственным словам, вот как это произошло:
Я ожидал, что новый Smalltalk будет знаковым языком, и его разработка займет как минимум два года, но в планы вмешалась судьба. В один прекрасный день, во время типичного мужского разговора в прихожей PARC, Тед Коэлер (Ted Kaehler), Дэн Инглз (Dan Ingalls) и я стояли и разговаривали о языках программирования. Пришло время обсудить мощь языка и теперь они интересовались, как сделать язык супермощным. Рисуясь перед ними, я сказал, что ‘самый мощный язык в мире’ они могут запечатлеть ‘в страницах с кодом’. Их реплика была: ‘создай либо заткнись’. Тед ушел обратно в CMU, но Дэн все еще был рядом и продолжал меня толкать дальше. Следующие две недели я приходил в PARC в четыре часа утра и работал до восьми, затем Дэн совместно с Генри Фуксом (Henry Fuchs), Джоном Шоком (John Shoch), и Стивом Перселом (Steve Purcell) начинали обсуждать утреннюю работу. Я заранее хвастался, потому что интерпретатор ЛИСП (LISP) Маккарти (John McCarthy) был написан на ЛИСП-е. Речь шла о той самой ‘странице с кодом’, и со временем, когда мощь языка росла, он становился всем для функциональных языков. Я был абсолютно уверен, что смогу сделать то же самое для объектно-ориентированных языков.Тут Кей ссылается на Джона Маккарти, который изобрел ЛИСП десятью годами ранее. Кею потребовалось всего восемь утр, чтобы закончить первую версию Smalltalk:
Первые несколько версий имели недостатки, которые громко критиковались группой. Но к восьмому утру, или где-то около того, код заработал…Я хотел бы быть таким же креативным, разносторонним и продуктивным какими Алан Кей и его коллеги по PARC были 40 лет назад.
Переменные класса в Smalltalk
Чтобы выяснить, как непосредственно переменные класса работают в Smalltalk, я установил GNU Smalltalk, версию языка, основанную на коммандной строке, которая легко скачивается и запускается под Linux Box. Вначале, Smalltalk показался мне очень странным и недружелюбным; его синтаксис немного странен на первый взгляд. Например, с точностью до конца помнить каждую команду, а также, определяя метод нужно указать только список аргументов…без имени метода! Я полагаю, что первый аргумент – это имя метода, или что-то вроде этого. Но через пару дней я привык к своеобразному синтаксису, и язык стал более осмысленным для меня.
Object subclass: Polygon [ Sides := 10. ] Polygon class extend [ sides [ ^Sides ] ] Polygon sides printNl. => 10class Polygon @@sides = 10 def self.sides @@sides end end puts Polygon.sides => 10Далее, небольшое объяснение, что код на Smalltalk делает:
• Object subclass: Polygon – это означает посылку подклассом сообщения классу Object и передача имени Polygon. Это создаст новый класс, который является подклассом класса Object. Это аналогия выражения class Polygon < Object в Руби. Конечно, в Руби, указание Object, как суперкласса необязательно.
• Sides := 10. – тут объявляется переменная класса Sides, и ей присваивается значение. Руби использует другой синтаксис: @@sides.
• Polygon class extend – тут ‘расширяется’ класс Polygon; т.е. класс Polygon открывается, чтобы дать мне возможность добавить метод класса. В Руби я использую конструкцию: class Polygon; def self.sides
• printNl метод выводит значение в консоль; это работает таким же образом, как puts в Руби, за исключением того, что метод printNl – это метод объекта Sides . Представьте только вызов @@sides.puts в Руби!Помимо поверхностных различий синтаксиса, если вы сделаете шаг назад и подумаете, то обнаружите, как удивительно похожи Smalltalk и Руби! Оба языка не только разделяют концепцию переменных класса, но и написание класса Polygon, объявление переменной класса и вывод значения в них одинаково. Фактически, вы можете думать о Руби, как о новой версии Smalltalk с упрощенным, и более удобным синтаксисом!
Polygon subclass: Triangle [ ] Triangle class extend [ set_sides: num [ Sides := num ] ] Polygon sides printNl. => 10class Triangle < Polygon def self.sides=(num) @@sides = num end end puts Triangle.sides =>10Triangle set_sides: 3. Triangle sides printNl. => 3Triangle.sides = 3 puts Triangle.sides => 3Polygon sides printNl. => 3puts Polygon.sides => 3Object subclass: Polygon [ Sides := 10. ] Polygon class extend [ sides [ ^Sides ] ] Polygon subclass: Triangle [ Sides := 3. ] Triangle class extend [ sides [ ^Sides ] ] Polygon sides printNl. >= 10 Triangle sides printNl. >= 3Это не так в Руби. Как мы видели выше, @@sides всегда ссылается на одно и то же значение.
Переменные экземпляра класса
В Руби, если вы хотите иметь отдельные значения для каждого класса, тогда вы должны использовать переменные экземпляра класса вместо переменных класса. Что это значит? Давайте взглянем на еще один пример Джона Нунмейкера:
class Polygon def self.sides @sides end @sides = 8 end puts Polygon.sides => 8
Теперь, когда я использую @sides вместо @@sides , Руби создает переменную экземпляра класса вместо переменной класса:
Концептуально нет никакой разницы, до тех пор, пока я не создам подкласс Triangle снова:
class Triangle < Polygon @sides = 3 endObject subclass: Polygon [ ] Polygon instanceVariableNames: 'Sides '! Polygon extend [ sides [ ^Sides ] ]class Polygon def sides @sides end endObject subclass: Polygon [ ] Polygon class instanceVariableNames: 'Sides '! Polygon class extend [ sides [ ^Sides ] ]class Polygon def self.sides @sides end endМетаклассы в Smalltalk и Руби
Давайте взглянем еще раз на строки кода, которые я использовал, чтобы создать переменные экземпляра класса в Smalltalk:
Polygon instanceVariableNames: 'Sides '!
В переводе с языка программирования на русский, это значит:
• Берем класс Polygon
• Шлем ему сообщение, называемое instanceVariableNames
• и передаем строку Sides , как параметр.Это пример того, как создавать переменные экземпляра в Smalltalk. Я буду создавать экземпляры класса Polygon , и все они будут иметь переменную экземпляра класса Sides . Другими словами, чтобы создать для каждого созданного экземпляра полигона переменную экземпляра класса, я вызываю метод на классе Polygon .
Как я объяснял выше, чтобы создать переменную экземпляра класса в Smalltalk, вы должны использовать ключевое слово 'class'. Например так:Polygon class instanceVariableNames: 'Sides '!Этот код означает буквально следующее: вызов метода instanceVariableNames на классе класса 'Polygon'. Действуя аналогичным образом, все экземпляры класса Polygon будут содержать переменную экземпляра класса. Но что значит: 'класс класса Polygon' в Smalltalk? Потратив несколько мгновений в GNU Smalltalk REPL, мы находим:
$ gst GNU Smalltalk ready st> Polygon printNl. => Polygon st> Polygon class printNl. => Polygon classВ примере, сперва, я вывожу объект класса Polygon . Затем, я пытаюсь узнать, что такое класс класса Polygon. И это 'Polygon class'. Но, какого типа этот объект? Давайте вызовем class, на нем:
st> Polygon class class printNl. => MetaclassАх… вот оно что. Класс класса – это метакласс. Выше, когда я вызывал instanceVariableNames , чтобы создать переменную экземпляра класса, я на самом деле использовал метакласс Polygon , экземпляр класса Metaclass .
Ниже, на диаграмме показано, как соотносятся все эти классы в Smalltalk:
![]()
Теперь, не должно быть сюрпризом, что Руби использует ту же самую модель. Вот как устроены классы внутри Руби:![]()
В Руби, когда бы вы не создали класс, Руби создает внутри соответствующий метакласс. В отличии от Smalltalk, Руби не использует это для переменных экземпляра класса, а только для отслеживания методов класса. Также, Руби не имеет класса Metaclass , но вместо всех метаклассов, создает экземпляры класса Class .В Руби метакласс спрятан, являясь 'таинственным понятием'. Руби молчаливо создает его, не говоря вам об этом, и не использует его напрямую. В Smalltalk, однако, метаклассы не спрятаны и играют огромную роль в языке. Создание переменной экземпляра класса, как показано выше, является только одним из примеров использования метаклассов в Smalltalk. Еще один хороший пример – это то, каким образом вы добавляете методы класса, вызывая extend .
Когда, вы запрашиваете класс класса в Руби, вы просто получаете Class. Руби ничего не говорит вам о метаклассах:
$ irb > class Polygon; end > Polygon.class ClassЧтобы увидеть метакласс Руби, попробуйте следующий трюк:
$ irb > class Polygon > def self.metaclass > class self > end > end > end => nil > Polygon.metaclass => #“#” — это и есть метакласс класса Polygon. Этот синтаксис обозначает ''экземпляр Class для класса Polygon' или метакласс для Polygon.



