stringtranslate.com

Синтаксис Ruby

Синтаксис языка программирования Ruby в целом похож на синтаксис Perl и Python . Определения классов и методов обозначаются ключевыми словами, тогда как блоки кода могут быть определены как ключевыми словами, так и фигурными скобками. В отличие от Perl, переменные не обязательно имеют префикс sigil . При использовании sigil изменяет семантику области действия переменной. Для практических целей нет различия между выражениями и операторами . [1] [2] Разрывы строк значимы и воспринимаются как конец оператора; точка с запятой может быть эквивалентно использована. В отличие от Python, отступы не имеют значения.

Одно из отличий от Python и Perl заключается в том, что Ruby сохраняет все свои переменные экземпляра полностью закрытыми для класса и раскрывает их только через методы доступа ( attr_writer, attr_reader, и т. д.). В отличие от методов «getter» и «setter» других языков, таких как C++ или Java , методы доступа в Ruby могут быть созданы с помощью одной строки кода с помощью метапрограммирования ; однако методы доступа также могут быть созданы традиционным способом C++ и Java. Поскольку вызов этих методов не требует использования скобок, легко изменить переменную экземпляра на полноценную функцию без изменения единой строки вызывающего кода или необходимости выполнять какой-либо рефакторинг, достигая функциональности, аналогичной членам свойств C# и VB.NET .

Дескрипторы свойств Python похожи, но в процессе разработки приходится идти на компромисс. Если начать в Python с использования публично открытой переменной экземпляра, а затем изменить реализацию, чтобы использовать закрытую переменную экземпляра, открытую через дескриптор свойства, может потребоваться скорректировать внутренний код класса для использования закрытой переменной, а не открытого свойства. Дизайн Ruby заставляет все переменные экземпляра быть закрытыми, но также предоставляет простой способ объявления методов setи getметодов. Это соответствует идее, что в Ruby никогда не осуществляется прямой доступ к внутренним членам класса извне класса; вместо этого классу передается сообщение и получается ответ.

Интерактивные сессии

Следующие примеры можно запустить в оболочке Ruby, например Interactive Ruby Shell , или сохранить в файле и запустить из командной строки, введя .ruby <filename>

Классический пример Hello world :

ставит «Привет, мир!» 

Немного базового кода Ruby:

# Все, включая литерал, является объектом, поэтому это работает: - 199 . abs # => 199 'ice is nice' . length # => 11 'ruby is cool.' . index ( 'u' ) # => 1 "Хороший день, не правда ли?" . downcase . split ( '' ) . uniq . sort . join # => " '?acdeinsty"   

Вход:

print 'Пожалуйста, введите имя >' name = gets . chomp puts "Привет #{ name } ."    

Конверсии:

puts 'Дайте мне число' число = получает . chomp puts число . to_i выходное_число = число . to_i + 1 puts выходное_число . to_s + ' большее число.'           

Струны

В Ruby существует множество способов определения строк.

Следующие задания эквивалентны:

a = " \n Это строка в двойных кавычках \n " a = %Q{ \n Это строка в двойных кавычках \n } a = %{ \n Это строка в двойных кавычках \n } a = %/\nЭто строка в двойных кавычках\n/ a = <<- BLOCK          Это строка BLOCK в двойных кавычках

Строки поддерживают интерполяцию переменных :

вар = 3 . 14159 "пи равно #{ var } " => "пи равно 3,14159"   

Следующие присваивания эквивалентны и создают необработанные строки :

a = 'Это строка в одинарных кавычках' a = %q{Это строка в одинарных кавычках}    

Коллекции

Построение и использование массива :

а = [ 3 , 'привет' , 14 . 5 , 1 , 2 , [ 6 , 15 ]]        a [ 2 ] # => 14,5 a . [] ( 2 ) # => 14,5 a . обратный # => [[6, 15], 2, 1, 14,5, 'привет', 3] a . flatten . uniq # => [3, 'привет', 14,5, 1, 2, 6, 15]    

Создание и использование ассоциативного массива (в Ruby называемого хешем ):

hash = Hash . new # эквивалентно hash = {} hash = { water : 'wet' , fire : 'hot' } # делает предыдущую строку избыточной, поскольку сейчас мы # назначаем hash новому, отдельному объекту hash puts hash [ :fire ] # печатает "hot"              hash . each_pair do | key , value | # или: hash.each do |key, value| puts " #{ key } is #{ value } " end # возвращает {:water=>"wet", :fire=>"hot"} и печатает: # вода мокрая # огонь горячий      hash . delete :water # удаляет пару :water => 'wet' и возвращает "wet" hash . delete_if { | key , value | value == 'hot' } # удаляет пару :fire => 'hot' и возвращает {}       

Структуры управления

Если утверждение:

# Сгенерировать случайное число и вывести, четное оно или нечетное. if rand ( 100 ) . even? puts "It's even" else puts "It's odd" end     

Блоки и итераторы

Два синтаксиса для создания блока кода:

{ puts 'Hello, World!' } # обратите внимание на фигурные скобки # или: do puts 'Hello, World!' end      

Блок кода может быть передан методу как необязательный аргумент блока. Многие встроенные методы имеют такие аргументы:

File . open ( 'file.txt' , 'w' ) do | file | # 'w' обозначает "режим записи" file . puts 'Написал какой-то текст.' end # файл автоматически закрывается здесь       Файл . readlines ( 'file.txt' ) . each do | line | puts line end # => Написал какой-то текст.    

Передача параметров блоку, который будет замыканием :

# В переменной экземпляра объекта (обозначенной как '@') запомните блок. def remember ( & a_block ) @block = a_block end    # Вызвать предыдущий метод, передав ему блок, который принимает имя. remember { | name | puts "Hello, #{ name } !" }   # Вызвать замыкание (обратите внимание, что это происходит не для замыкания каких-либо свободных переменных): @block . call ( 'Jon' ) # => "Привет, Джон!" 

Создание анонимной функции :

proc { | arg | puts arg } Proc . new { | arg | puts arg } lambda { | arg | puts arg } -> ( arg ) { puts arg } # введено в Ruby 1.9            

Возврат замыканий из метода:

def create_set_and_get ( initial_value = 0 ) # обратите внимание, что значение по умолчанию равно 0 closure_value = initial_value [ Proc . new { | x | closure_value = x }, Proc . new { closure_value } ] end                setter , getter = create_set_and_get # возвращает два значения setter.call ( 21 ) getter.call # = > 21     # Переменные параметров также могут использоваться в качестве привязки для замыкания, # поэтому предыдущее можно переписать так:def create_set_and_get ( closure_value = 0 ) [ proc { | x | closure_value = x } , proc { closure_value } ] конец              

Передача потока управления программой блоку, который был предоставлен во время вызова:

def use_hello yield "привет" конец   # Вызвать предыдущий метод, передав ему блок. use_hello { | string | puts string } # => 'hello'    

Итерация по перечислениям и массивам с использованием блоков:

array = [ 1 , 'привет' , 3 . 14 ] array . each { | item | puts item } # выводит: # 1 # 'привет' # 3.14        array . each_index { | index | puts " #{ index } : #{ array [ index ] } " } # выводит: # 0: 1 # 1: 'привет' # 2: 3.14    # В следующем примере используется диапазон (a..b) ( 3..6 ) . each { | num | puts num } # выводит: # 3 # 4 # 5 # 6    # В следующем примере используется диапазон (a...b) ( 3 ... 6 ) . each { | num | puts num } # выводит: # 3 # 4 # 5    

Метод, такой как injectможет принимать как параметр, так и блок. injectМетод выполняет итерацию по каждому члену списка, выполняя некоторую функцию над ним, сохраняя при этом агрегат. Это аналогично функции foldlв языках функционального программирования . Например:

[ 1 , 3 , 5 ] .inject ( 10 ) { | сумма , элемент | сумма + элемент } # => 19      

На первом проходе блок получает 10 (аргумент для внедрения) как sum, и 1 (первый элемент массива) как element. Это возвращает 11, который затем становится sumна следующем проходе. Он добавляется к 3, чтобы получить 14, который затем добавляется к 5 на третьем проходе, чтобы в итоге вернуть 19.

Использование перечисления и блока для возведения в квадрат чисел от 1 до 10 (используя диапазон ):

( 1 .. 10 ) . собрать { | x | x * x } # => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]   

Или вызвать метод для каждого элемента ( mapявляется синонимом collect):

( 1 .. 5 ) . карта ( & :to_f ) # => [1.0, 2.0, 3.0, 4.0, 5.0] 

Классы

Следующий код определяет класс с именем Person. В дополнение к initialize, обычному конструктору для создания новых объектов, он имеет два метода: один для переопределения <=>оператора сравнения (чтобы Array#sortможно было сортировать по возрасту), а другой для переопределения to_sметода (чтобы Kernel#putsможно было форматировать его вывод). Вот attr_readerпример метапрограммирования в Ruby: attr_accessorопределяет методы getter и setter переменных экземпляра, но attr_readerтолько методы getter. Последний вычисляемый оператор в методе — это его возвращаемое значение, что позволяет опустить явный returnоператор.

class Person attr_reader :name , :age def initialize ( name , age ) @name , @age = name , age end def <=> ( person ) # оператор сравнения для сортировки @age <=> person . age end def to_s " #{ @name } ( #{ @age } )" end end                        группа = [ Человек . новый ( "Боб" , 33 ), Человек . новый ( "Крис" , 16 ), Человек . новый ( "Эш" , 23 ) ]        ставит группу . сортировку . обратный 

Приведенный выше код выводит три имени в обратном порядке возраста:

Боб (33)Ясень (23)Крис (16)

Personявляется константой и является ссылкой на Classобъект.

Открытые занятия

В Ruby классы никогда не закрываются: методы всегда можно добавить к существующему классу. Это применимо ко всем классам, включая стандартные встроенные классы. Все, что нужно сделать, это открыть определение класса для существующего класса, и указанное новое содержимое будет добавлено к существующему содержимому. Простой пример добавления нового метода к классу стандартной библиотеки Time:

# повторно открыть класс времени Ruby class Time def yesterday self - 86400 end end       сегодня = Время . сейчас # => 2013-09-03 16:09:37 +0300 вчера = сегодня . вчера # => 2013-09-02 16:09:37 +0300      

Добавление методов к ранее определенным классам часто называют monkey-patching . Если это делается безрассудно, то это может привести как к конфликтам поведения с последующими неожиданными результатами, так и к проблемам масштабируемости кода.

Начиная с Ruby 2.0 стало возможным использовать уточнения для уменьшения потенциально негативных последствий «мани-патчинга» путем ограничения области действия патча определенными областями кодовой базы.

# повторно открыть модуль класса времени Ruby RelativeTimeExtensions refine Time do def half_a_day_ago self - 43200 end end end           модуль MyModule класс MyClass # Разрешить использование уточнения с помощью RelativeTimeExtensions       def window Время . сейчас . полдня_назад конец конец конец    

Исключения

Исключение возникает при raiseвызове:

поднимать

К исключению можно добавить необязательное сообщение:

поднять "Это сообщение" 

Исключения также могут быть указаны программистом:

raise ArgumentError , "Недопустимые аргументы!"  

В качестве альтернативы можно передать методу экземпляр исключения raise:

raise ArgumentError . new ( "Недопустимые аргументы!" ) 

Последняя конструкция полезна при создании экземпляра пользовательского класса исключений, содержащего конструктор, который принимает более одного аргумента:

class ParseError < Exception def initialize ( input , line , pos ) super "Не удалось проанализировать ' #{ input } ' в строке #{ line } , позиция #{ pos } " end end          вызвать ParseError . новый ( "Foo" , 3 , 9 )   

Исключения обрабатываются предложением rescue. Такое предложение может перехватывать исключения, которые наследуются от StandardError. Другие ключевые слова управления потоком, которые можно использовать при обработке исключений, это elseи ensure:

begin # сделать что-то rescue # обработать исключение else # сделать это, если исключение не было вызвано ensure # сделать это независимо от того, было ли вызвано исключение end    

Распространенной ошибкой является попытка перехватить все исключения с помощью простого спасательного предложения. Чтобы перехватить все исключения, нужно написать:

begin # сделайте что-нибудь rescue Exception # Здесь код обработки исключений. # Не пишите только "rescue"; это перехватывает только StandardError, подкласс Exception. end    

Или перехватывайте определенные исключения:

begin # сделайте что-нибудь, спасите RuntimeError # обработайте только RuntimeError и его подклассы end   

Также можно указать, что объект исключения должен быть доступен для предложения обработчика:

begin # сделайте что-нибудь спасательное RuntimeError => e # обработка, возможно, связанная с e, например "puts e.to_s" end     

В качестве альтернативы последнее исключение сохраняется в магической глобальной переменной $!.

Также можно выделить несколько исключений:

begin # сделать что-нибудь спасательное RuntimeError , Timeout :: Error => e # обработка, возможно, с участием e end      

Метапрограммирование

Код Ruby может программно изменять во время выполнения аспекты своей собственной структуры, которые были бы фиксированы в более жестких языках, таких как определения классов и методов. Этот вид метапрограммирования может использоваться для написания более краткого кода и эффективного расширения языка.

Например, следующий код Ruby генерирует новые методы для встроенного Stringкласса на основе списка цветов. Методы оборачивают содержимое строки в HTML-тег, стилизованный под соответствующий цвет.

ЦВЕТА = { черный : "000" , красный : "f00" , зеленый : "0f0" , желтый : "ff0" , синий : "00f" , пурпурный : "f0f" , голубой : "0ff" , белый : "fff" }                   класс String ЦВЕТА . каждый do | цвет , код | define_method "in_ #{ цвет } " do "<span style= \" цвет: # #{ код } \" > #{ сам } </span>" конец конец конец          

Сгенерированные методы затем можно использовать следующим образом:

"Привет, мир!" . in_blue => "<span style= \" color: #00f \" >Привет, мир!"</span>"  

Чтобы реализовать эквивалент во многих других языках, программисту пришлось бы писать каждый метод ( in_black, in_red, in_green, и т. д.) отдельно.

Вот некоторые другие возможные варианты использования метапрограммирования Ruby:

Ссылки

  1. ^ "[ruby-talk:01120] Re: Значение while..." В синтаксисе Ruby оператор — это всего лишь частный случай выражения, которое не может использоваться в качестве аргумента (например, множественное присваивание).
  2. ^ "[ruby-talk:02460] Re: Вопрос о приоритете". оператор [...] не может быть частью выражения, если он не заключен в скобки.