В Ruby из других языков

Если вы впервые посмотрите на Ruby код, он скорее всего напомнит вам некоторые используемые вами языки. Это не случайно. Большинство синтаксических конструкций покажутся знакомыми пользователям Perl, Python и Java, так что, если вы уже писали на них, изучить Ruby окажется проще простого.

Эта страница состоит из двух частей. В первой содержится попытка сверхкратко описать, что вас ждет при переходе с языка Х на Ruby. Вторая рассказывает об основных особенностях языка, и как эти особенности соотносятся с тем, что вы уже знаете.

Чего ожидать после языка Х на Ruby

Важные замечания по поводу языка и подсказки

Тут собрано несколько подсказок и советов по основным особенностям Ruby, которые вы увидите по мере изучения языка

Итерации и циклы

Две особенности Ruby, отличающиеся от всего ранее увиденного, но к которым надо привыкнуть - это “блоки” и итераторы. Вместо того, чтобы итерироваться по индексу (как в С, С++ и pre-1.5 Java), или по списку (как в Perl for (@a) {...}, или в Python for i in aList: ...), в Ruby зачастую вы увидите

some_list.eachdo|this_item|# Внутри "блока"# у нас есть объект this_item.end

За более подробной информацией о each (и сопутствующих collect, find, inject, sort, и т.д.), обращайтесь к ri Enumerableri Enumerable#имя_функции).

Все имеет значение

В Ruby нет разницы между выражением и оператором. Все имеет значение, даже если это значение - nil. Вот так:

x=10y=11z=ifx<ytrueelsefalseendz# => true

Символы - это не “легковесные” строки

Многие начинающие натыкаются на проблему понимания, что такое “символ” в Ruby, и для чего он предназначен.

Символы лучше всего сравнить с уникальными идентификаторами. Символ это скорее сам знак, а не то, что он означает. Запустите irb, чтобы почувствовать разницу:

irb(main):001:0>:george.object_id==:george.object_id=>trueirb(main):002:0>"george".object_id=="george".object_id=>falseirb(main):003:0>

Метод object_id возвращает уникальный идентификатор объекта. Если два объекта имеют одинаковый object_id, то это один и тот же объект (указатель на один объект в памяти).

Как вы видите, как только вы используете символ, любой другой символ, идентичный по написанию с первым, будет обращаться к тому же объекту в памяти. У всех “символов” с одинаковым набором букв (с одним именем) один и тот же object_id.

Теперь взглянем на строки. Их object_id не совпадает. Это означает, что это два разных объекта в памяти. При создании строки Ruby всегда выделяет память для нее.

Если вы сомневаетесь, что использовать - символ или строку - задумайтесь, что важнее: уникальность объекта (например, для ключа в хеше) или содержание (как в примере выше - “george”)

Все является объектом

“Все - это объект” - не преувеличение. Даже классы и числа, и с ними можно делать то, что и с обычными объектами:

# То же самое, что и# class MyClass# attr_accessor :instance_var# endMyClass=Class.newdoattr_accessor:instance_varend

Изменяемые константы

Константы на самом деле не константы. Если вы поменяете значение константы, это выдаст предупреждение, но не остановит программу. Однако это не говорит о том, что вы должны переопределять константы.

Соглашение о наименовании

Ruby диктует некоторые правила о наименовании. Константы начинаются с заглавной буквы. Глобальные переменные начинаются со знака $. Переменные экземпляра начинаются с @. Переменные класса начинаются с @@.

Имена методов могут начинаться с заглавных букв, однако это может запутать, например:

Constant=10defConstant11end

Значение Constant - 10, а Constant() - 11.

Именованные параметры

Как и в Python, начиная с версии 2.0 Ruby методы принимают именованные параметры.

defdeliver(from: "A",to: nil,via: "mail")"Sending from #{from} to #{to} via #{via}."enddeliver(to: "B")# => "Sending from A to B via mail."deliver(via: "Pony Express",from: "B",to: "A")# => "Sending from B to A via Pony Express."

“Истинное” Ruby

В Ruby все кроме nil и false рассматриваются как истина. В С, Python и многих других языках 0 и некоторые другие значения, например пустой список, являются ложью. Взгляните на следующий код Python (пример применим и у другим языкам):

# Python if0:print("0 - истина")else:print("0 - ложь")

Это выведет на экран “0 - ложь”. Эквивалент на Ruby:

# Rubyif0puts"0 - истина"elseputs"0 - ложь"end

Выведет на экран “0 - истина”.

Модификаторы доступа действуют до конца контекста

В следующем Ruby коде

classMyClassprivatedefa_method;true;enddefanother_method;false;endend

можно подумать, что another_method публичный. Нет, это не так. Модификатор доступа private действует до конца контекста, или до другого модификатора доступа. По умолчанию все методы публичны.

classMyClass# a_method публичныйdefa_method;true;endprivate# another_method приватныйdefanother_method;false;endend

public, private и protected на самом деле методы, и они могут принимать параметры. Можно передать им имена методов как символы, чтобы поменять область доступа метода.

Вызов методов

В Java public означает, что метод можно вызвать везде. protected методы можно вызвать только инстансами этого класса, инстансами дочернего класса и инстансами классов этого же пакета. private методы не может вызвать никто кроме инстанса класса.

В Ruby все немного по-другому. public методы на самом деле публичные. private метод может быть вызван только без явного объявления вызывающей стороны. Только self может быть вызывающей стороной приватного метода.

О protected методах надо поговорить подробнее. Protected метод может быть вызван инстансом текущего или дочернего класса, однако может иметь вызывающей стороной другой инстанс. Пример, позаимствованный из Ruby Language FAQ:

classTest# публичный метод по умолчаниюdefidentifier99enddef==(other)identifier==other.identifierendendt1=Test.new# => #<Test:0x34ab50>t2=Test.new# => #<Test:0x342784>t1==t2# => true# сделаем `identifier' protected методом# это возможно, потому что можно вызвать метод у объекта otherclassTestprotected:identifierendt1==t2# => true# теперь сделаем `identifier' приватнымclassTestprivate:identifierendt1==t2# NoMethodError: private method `identifier' called for #<Test:0x342784>

Открытые классы

Классы в Ruby “открыты”. То есть, вы можете открыть их и добавить или изменить их в любое время. Даже базовые классы, такие как Integer или Object, родительский для всех объектов. Ruby on Rails определяет несколько методов на Integer, чтобы работать со временем. Смотрите:

classIntegerdefhoursself*3600# число секунд в 1 часеendaliashourhoursend# 14 часов после 00:00, 1 января# (когда все только-только просыпаются ;)Time.mktime(2006,01,01)+14.hours# => Sun Jan 01 14:00:00

Прикольные имена методов

В Ruby имена методов могут оканчиваться на вопросительный или восклицательный знаки. По соглашению методы, которые отвечают на вопрос, заканчиваются вопросительным знаком (например, Array#empty? возвращает true если массив пустой). Некоторые, “потенциально опасные” методы (которые меняют вызывающую сторону, self или параметры) заканчиваются восклицательным знаком (например, exit!). Однако не все методы, которые меняют аргументы заканчиваются так, например Array#replace заменяет содержимое массива переданным массивом. Просто нет смысла иметь метод, который бы не менял исходный массив в этом случае.

Singleton методы

Singleton методы - это методы, определенные на единственном инстансе и доступные только на нем.

classCardefinspect"Cheap car"endendporsche=Car.newporsche.inspect# => Cheap cardefporsche.inspect"Expensive car"endporsche.inspect# => Expensive car# Другие объекты не изменяют поведениеother_car=Car.newother_car.inspect# => Cheap car

“Пропавшие” методы

Ruby не сдается, если не находит вызванный метод, а вызывает метод method_missing, передав ему имя “потерянного” метода и аргументы. По умолчанию method_missing вызывает исключение NameError, но вы можете переопределить его по вашим потребностям, что и делает множество библиотек, например:

# id - имя вызванного метода, *arguments - такой синтаксис# передает все аргументы в функцию как массив 'arguments'defmethod_missing(id,*arguments)puts"Метод #{id} был вызван, но не найден."+"Его аргументы: #{arguments.join(", ")}"end__:a,:b,10# => Метод __ был вызван, но не найден.# Его аргументы: a, b, 10

Пример выше просто выводит на экран детали вызова метода, но вы можете сделать что-то полезное по вашему усмотрению.

Передача сообщений, а не вызов функций

На самом деле вызов метода - это передача (send) ему сообщения. Наглядно:

# Это1+2# то же самое, что и ...1.+(2)# и то же самое, что и:1.send"+",2

Блоки - тоже объекты, только они об этом еще не знают

Блоки (на самом деле - замыкания) часто используются в стандартной библиотеке. Чтобы вызвать блок можно либо использовать yield, либо сделать его объектом класса Proc, прибавив специальный аргумент к списку аргументов, например так:

defblock(&the_block)# Тут the_block это блок, переданный методуthe_block# вернет блок (как объект)endadder=block{|a,b|a+b}# adder - это объект класса Procadder.class# => Proc

Вы можете создавать блоки-объекты также через Proc.new с блоком или вызывая lambda метод.

В принципе, методы - это тоже объекты:

method(:puts).call"puts is an object!"# => puts is an object!

Операторы - это синтаксический сахар

Большинство операторов в Ruby - это просто синтаксический сахар (с учетом некоторых правил) для вызова методов. Например, можно переопределить + метод для класса Integer:

classInteger# Так можно, но не значит, что нужно ;)def+(other)self-otherendend

Так что вам не потребуется operator+ из С++, и т.д.

А еще можно симулировать обращения к объекту как к массиву с помощью методов [] и []=. Можно определить унарные операторы + и - (например +1 или -2) методами +@ и -@ соответственно. Операторы ниже, однако, не являются синтаксическим сахаром. Они не являются методами и не могут быть переопределены:

=,..,...,not,&&,and,||,or,::

В дополнение к этому, +=, *= и т.д. - это всего лишь сокращения для var = var + other_var, var = var * other_var и т.д. и, соответственно, не могут быть переопределены.

Узнать больше

Если вам хочется узнать о Ruby больше - перейдите к документации.



close