Основные грани Ruby. От главных конструкций до приложения::Журнал СА 102009
www.samag.ru
Льготная подписка для студентов      
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
О журнале
Журнал «БИТ»
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

Jobsora

ЭКСПЕРТНАЯ СЕССИЯ 2019


  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
28.05.2019г.
Просмотров: 1826
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 1887
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 1446
Комментарии: 0
Django 2 в примерах

 Читать далее...

28.05.2019г.
Просмотров: 1066
Комментарии: 0
Введение в анализ алгоритмов

 Читать далее...

27.03.2019г.
Просмотров: 1636
Комментарии: 1
Arduino Uno и Raspberry Pi 3: от схемотехники к интернету вещей

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 Основные грани Ruby. От главных конструкций до приложения

Архив номеров / 2009 / Выпуск №10 (83) / Основные грани Ruby. От главных конструкций до приложения

Рубрика: Карьера/Образование /  Исследование

ДМИТРИЙ ВАСИЛЬЕВ, больше 10 лет профессионально занимается разработкой ПО, принимает активное участие в различных проектах с открытым исходным кодом

Основные грани Ruby
От главных конструкций до приложения

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

Особенности Ruby

Объектно-ориентированный язык. Каждый элемент в языке является объектом, и результат манипуляций с объектами также является объектом.

Поддержка автоматической «сборки мусора». Программисту не надо заботиться об освобождении памяти, она освобождается автоматически.

Поддержка исключений. Исключения позволяют изменять основной путь выполнения программы в случае возникновения каких-либо ошибочных ситуаций.

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

Регулярные выражения на уровне языка. Регулярные выражения предоставляют стандартный способ работы с шаблонами для текста. Ruby позволяет создавать регулярные выражения на уровне синтаксиса языка, как, например, Perl или JavaScript.

Мультиплатформенность. Интерпретатор Ruby можно скачать практически для всех основных платформ, включая Windows, Linux, Mac OS X. Или он может быть собран из исходного кода.

Поддержка интроспекции и изменения объектов. Ruby позволяет исследовать и изменять объекты во время выполнения. В том числе, есть возможность изменять даже встроенные в язык классы.

Удобный синтаксис. Как и SmallTalk, Ruby позволяет не писать скобки при вызове методов, что делает его удобным для создания микроязыков.

Большая стандартная библиотека. Как и Python, Ruby поставляется с большим количеством стандартных пакетов.

Проекты, использующие Ruby

Ruby используется как скриптовый язык многими коммерческими компаниями, например NASA, Motorola и Lucent. Многие сайты используют Ruby как основной язык разработки, например:

Basecamp (http://www.basecamphq.com) – веб-приложение для управления проектами.

Twitter (http://twitter.com) – сервис микроблоггинга.

GitHub (http://github.com) – хостинг проектов, использующих систему контроля версий Git.

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

Ruby on Rails (http://www.rubyonrails.ru) – фреймворк для создания веб-проектов.

Capistrano (http://www.capify.org) – инструментарий для запуска скриптов на нескольких серверах.

RubyGems (http://docs.rubygems.org) – менеджер пакетов для Ruby.

Интерактивная оболочка

В примерах интерактивных сессий ниже будет использоваться интерактивная оболочка для Ruby – Interactive Ruby Shell (irb). Она входит в официальный дистрибутив Ruby, но в случае использования стандартной системы пакетов операционной системы irb может быть в отдельном от основного интерпретатора пакете и может потребовать отдельной установки. Попробуйте запустить оболочку командой irb (на Windows путь к оболочке должен быть установлен в переменной среды PATH), если вы увидите следующее приглашение, то интерпретатор и оболочка Ruby уже установлены в системе:

irb(main):001:0>

В противном случае установите интерпретатор и оболочку либо с помощью стандартной системы пакетов операционной системы, либо скачав инсталлятор или исходный код с официального сайта: http://www.ruby-lang.org/en/downloads.

Основные типы данных

Запустим интерактивную оболочку Ruby и начнем экспериментировать с основными типами данных.

Числа и логические операции

Рассмотрим пример:

irb(main):001:0> (10 == 012) && (10 == 0xa) && (10 == 0b1010)

=> true

irb(main):002:0> 12345678901234567890 / 4.5

=> 2.74348420027435e+18

irb(main):003:0> -33 == 33

=> false

 Надо сразу заметить, что трехзначное число в приглашении оболочки показывает номер текущей строки, в нашем случае – это 001, 002 и 003. В первой строке показаны различные способы записи числа 10:

012 – в восьмеричной системе счисления;

0xa – в шестнадцатеричной системе счисления;

0b1010 – в двоичной системе счисления.

В примере числа в каждой из систем счисления сравниваются на равенство с числом 10, и в результате возвращается (объект после знака =>) логическое true «истина».

Во второй строке длинное целое (Ruby автоматически поддерживает работу с целыми, больше размера машинного слова) делится на вещественное число и в результате получается вещественное число, которое выводится в экспоненциальной форме.

В третьей строке мы сравниваем отрицательное целое с положительным и, естественно, получаем в итоге логическое false «ложь».

В логических операциях в качестве значения false «ложь» могут выступать только предопределенные константы false и nil «нулевой объект». Все остальные объекты будут представлять из себя «истину».

Строки

Пример со строками:

irb(main):001:0> ("test" == 'test') && ("test" == %q/test/)

=> true

irb(main):002:0> <<END

irb(main):003:0" test

irb(main):004:0" END

=> "test\n"

irb(main):005:0> "test\n" != 'test\n'

=> true

irb(main):006:0> "#{10 * 10} times"

=> "100 times"

irb(main):007:0> '#{10 * 10} times'

=> "\#{10 * 10} times"

В первой строке показаны три способа создания строк – двойные кавычки, одинарные кавычки и %q. Последний способ %q позволяет использовать практически любые ограничители для строк и соответствует одинарным кавычкам (%Q соответствует двойным кавычкам). Строки в двойных кавычках позволяют использовать специальные символы, например перевод строки – \n. Строки в одинарных кавычках передают все символы «как есть». Различие между использованием одинарных и двойных кавычек показано в строках с 5-й по 7-ю. Для многострочных строк удобно использовать способ, показанный в строках со 2-й по 4-ю. Текст-ограничитель после символов << должен совпадать с текстом в конце строки и не входит в состав результирующей строки. Внутрь строк удобно включать небольшие выражения с помощью конструкции #{выражение}, как показано в строке 6. Но, как было описано выше, этот способ не работает для строк в одинарных кавычках.

Массивы

Обычные массивы индексируются по номерам элементов и создаются с помощью квадратных скобок:

irb(main):001:0> [1, 4.5, "test"]

=> [1, 4.5, "test"]

irb(main):002:0> [[1, 2], [3, 4]]

=> [[1, 2], [3, 4]]

irb(main):003:0> [[1, 2], [3, 4]][0]

=> [1, 2]

irb(main):004:0> [[1, 2], [3, 4]][-1]

=> [3, 4]

Как видно из примеров, массивы могут содержать смешанные элементы. В первой строке мы создали массив, содержащий целое и вещественные числа и строку. Во второй строке мы создали массив, содержащий два других массива, которые в свою очередь содержат целые числа.

Элементы в массивах доступны по индексам, при этом индексы могут быть отрицательными, и в этом случае отсчет будет идти с конца массива. Примеры в строках 3 и 4 иллюстрируют доступ к элементам массива.

Ассоциативные массивы

Ассоциативные массивы (хэши) индексируются не по номеру элемента, а практически любым объектом-ключом и создаются с помощью фигурных скобок:

irb(main):001:0> {"one" => 1, 2 => "two"}

=> {2=>"two", "one"=>1}

irb(main):002:0> {"one" => 1, 2 => "two"}["one"]

=> 1

irb(main):003:0> {"one" => 1, 2 => "two"}[2]

=> "two"

irb(main):004:0> {"one" => 1, 2 => "two"}["2"]

=> nil

В первой строке мы создали хэш, состоящий из двух элементов 1 и «two», которые индексируются соответственно строкой «one» и целым 2. Примеры в строках 2, 3 и 4 показывают доступ к элементам хэша. При этом запрос несуществующего элемента «2» (Ruby – это язык со строгой типизацией и не преобразует автоматически типы данных) возвращает «нулевой объект» nil.

Диапазоны значений

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

irb(main):001:0> 1..10

=> 1..10

irb(main):002:0> "a"..."z"

=> "a"..."z"

irb(main):003:0> (1..10).to_a

=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

irb(main):004:0> ("a"..."f").to_a

=> ["a", "b", "c", "d", "e"]

При этом, если начало и конец диапазона разделены двумя точками, то последнее значение будет включено в диапазон, а в случае трех точек – исключено.

Немного забегая вперед, в строках 3 и 4 мы используем вызов метода to_a(), преобразующего диапазон в массив. Здесь видно, что для доступа к методам объектов используется оператор «.» и для метода необязательно указывать скобки.

Переменные и управляющие конструкции

Для имен переменных Ruby использует префиксы, помогающие определить использование (или область видимости) имени:

  •  Локальные переменные, параметры методов и имена методов все должны начинаться с маленькой латинской буквы или знака подчеркивания.
  •  Глобальные переменные должны начинаться со знака $.
  •  Переменные экземпляров классов должны начинаться со знака @ и доступны только внутри объекта-экземпляра.
  •  Переменные классов должны начинаться с двух знаков @@ и разделяются между всеми экземплярами класса.
  •  Имена классов, модулей и константы должны начинаться с большой латинской буквы.

Рассмотрим небольшой пример:

irb(main):001:0> PI = 3.1415

=> 3.1415

irb(main):002:0> PI = 3.1415

 

(irb):2: warning: already initialized constant PI

=> 3.1415

 

irb(main):003:0> $count = 0

=> 0

irb(main):004:0> (1..10).each {|i| $count += i}

=> 1..10

irb(main):005:0> $count

=> 55

В первой строке мы создаем константу PI. В случае ее переопределения выводится предупреждение, хотя ее все еще можно переопределить.

В третьей строке мы создаем глобальную переменную $count. В четвертой строке используется новая конструкция Ruby – блок. В левой части вызывается метод-итератор each уже известного нам типа данных – диапазона значений. В правой части в фигурных скобках описывается блок кода, где между вертикальными чертами указывается переменная для подстановки, и затем код для выполнения. Полное выражение делает следующее: метод each вызывает блок кода для каждого значения в диапазоне от 1 до 10 и подставляет значение в локальную переменную i, после чего переменная складывается с глобальной переменной $count. Как мы видим, в пятой строке значение глобальной переменной содержит сумму всех элементов в диапазоне от 1 до 10.

Кроме фигурных скобок блоки кода могут быть определены с помощью конструкции do/end, удобной для многострочных блоков:

irb(main):001:0> (1..10).each do |i|

irb(main):002:1* printf "#{i} "

irb(main):003:1> end

1 2 3 4 5 6 7 8 9 10 => 1..10

Заметьте, что у выражения есть также и возвращаемое значение (1..10).

Условные выражения

Выражение if..then/end позволяет выполнять ветки кода в зависимости от условий:

irb(main):001:0> n = 100

=> 100

irb(main):002:0> if n == 100 then

irb(main):003:1* "hundred"

irb(main):004:1> elsif n == 10 then

irb(main):005:1* "ten"

irb(main):006:1> else

irb(main):007:1* "one"

irb(main):008:1> end

=> "hundred"

Также есть выражение «обратное» if – unless..then/end:

irb(main):001:0> n = 100

=> 100

irb(main):002:0> unless n == 100 then

irb(main):003:1* "ten"

irb(main):004:1> else

irb(main):005:1* "hundred"

irb(main):006:1> end

=> "hundred"

Выражение case/end сравнивает переменную по нескольким условиям:

irb(main):001:0> n = 100

=> 100

irb(main):002:0> case n

irb(main):003:1> when 1...10 then 1

irb(main):004:1> when 10...100 then 2

irb(main):005:1> when 100...1000 then 3

irb(main):006:1> end

=> 3

Циклы

Кроме использования итераторов и блоков кода, Ruby предоставляет и более «классические» конструкции для создания циклов.

Конструкция while/end выполняет тело цикла, пока выражение не вернет «ложь» (необходимо помнить, что в качестве значения «ложь» можно использовать только false или nil):

irb(main):001:0> while line = gets

irb(main):002:1> printf "Line: #{line}"

irb(main):003:1> end

line1

Line: line1

line2

Line: line2

=> nil

Здесь в качестве выражения используется функция gets, читающая данные со стандартного устройства ввода. Завершить ввод данных можно набрав «конец файла» – <Ctrl>+<D> (<Ctrl>+<Z> для Windows).

Конструкция until/end является «обратной» while и выполняет тело цикла, пока не будет получено значение «истина»:

irb(main):001:0> i = 0

=> 0

irb(main):002:0> until i > 5

irb(main):003:1> i += 1

irb(main):004:1> end

=> nil

irb(main):005:0> i

=> 6

И, наконец, конструкция for..in/end позволяет выполнить итерацию по какому-либо объекту:

irb(main):001:0> for i in [100, 10, 1]

irb(main):002:1> printf "#{i} "

irb(main):003:1> end

100 10 1 => [100, 10, 1]

Классы, объекты и методы

Рассмотрим классы, объекты и методы на примере небольшой иерархии, состоящей из одного абстрактного базового класса Shape и двух его наследников – Circle и Square. Создадим файл area.rb со следующим содержимым:

class Shape

    def area()

        raise "Method not implemented"

    end

end

class Circle < Shape

    attr_reader :radius

    def initialize(radius)

        @radius = radius

    end

    def area()

        Math::PI * @radius

    end

end

class Square < Shape

    attr_reader :side

    def initialize(side)

        @side = side

    end

    def area()

        @side * @side

    end

end

Базовый класс Shape содержит один метод area(), который при вызове поднимает исключение RuntimeError с параметром Method not implemented.

Класс Circle наследуется от класса Shape. Первая строка класса (attr_reader) служит для автоматического создания метода, возвращающего значение переменной экземпляра @radius (по умолчанию переменные доступны только внутри экземпляра класса). Метод initialize – это конструктор класса, получающий переменную radius и сохраняющий ее значение в переменной экземпляра @raidus. Метод area использует константу из стандартного модуля Math для вычисления площади круга.

Класс Square так же наследуется от класса Shape и похож на класс Circle, за исключением реализации метода area.

Рассмотрим работу классов в интерактивной сессии:

irb(main):001:0> require "area"

=> true

irb(main):002:0> s = Shape.new

=> #<Shape:0x7f82a7812028>

irb(main):003:0> s.area

RuntimeError: Method not implemented

    from ./area.rb:3:in `area'

    from (irb):3

    from :0

irb(main):004:0> c = Circle.new 10

=> #<Circle:0x7f82a77ff860 @radius=10>

irb(main):005:0> c.radius

=> 10

irb(main):006:0> c.radius = 20

NoMethodError: undefined method `radius=' for

#<Circle:0x7f82a77ff860 @radius=10>

    from (irb):6

    from :0

irb(main):007:0> c.area

=> 31.4159265358979

irb(main):008:0> sq = Square.new 10

=> #<Square:0x7f82a77e43a8 @side=10>

irb(main):009:0> sq.side

=> 10

irb(main):010:0> sq.area

=> 100

В первой строке мы загружаем наш файл с помощью метода require, который возвращает true в случае успеха. Затем мы создаем экземпляр класса Shape с помощью его метода new. В строке 3 мы пробуем выполнить метод area для экземпляра класса Shape, но, как и ожидалось, метод выкидывает исключение RuntimeError. В строке 4 мы создаем экземпляр класса Circle и в следующей строке запрашиваем значение атрибута radius. Конечно, атрибут нельзя присвоить, так как не был создан соответствующий метод присвоения. Метод area возвращает площадь круга в строке 7. Строки с 8-й по 10-ю создают экземпляр класса Square и вызывают его методы.

Создаем приложение

После того как мы создали несколько классов, воспользуемся ими для создания небольшого приложения, которое позволит вычислять площадь объектов в интерактивном режиме. Добавим в файл area.rb следующий код:

@shapes = {

    "circle" => Circle,

    "square" => Square

}

def area(line)

    parts = line.split

    raise "Wrong number of parameters" if parts.length < 2

    shape = @shapes.fetch parts[0], nil

    raise "Unknown shape" if !shape

    arity = shape.allocate.method(:initialize).arity

    sizes = parts[1..-1].map {|i| i.to_f}

    raise "Wrong number of arguments" if arity != sizes.length

    shape.new(*sizes).area

end

def main()

    begin

        print "> "

        begin

            break if !line = gets

            puts area line

        rescue Interrupt

            break

        rescue RuntimeError => why

            print "?#{why}\n"

        end

    end while true

end

main

Этот код добавляет один хэш-массив, который сопоставляет имена объектов с их классами, и две функции:

  • main – основная функция приложения, вызов которой производится в самом конце файла. Функция содержит бесконечный цикл, выход из которого осуществляется оператором break, либо в случае признака «конец файла» (<Ctrl>+<D>, или <Ctrl>+<Z> для Windows), либо в случае прерывания выполнения с помощью <Ctrl>+<C>. Внутри бесконечного цикла функция gets читает строку, введенную пользователем, и вычисляет площадь объекта с помощью функции area. В случае ошибки в функции area она перехватывается оператором rescue RuntimeError и ее описание выводится на экран.
  • area – вычисляет площадь объекта по строке, введенной пользователем. Сначала строка разбивается на части по пробелам, или табуляциям. При этом число частей должно быть больше двух. Затем первый элемент массива используется для получения нужного класса вычисления площади из хэш-массива @shapes. После этого все остальные аргументы преобразуются в вещественные числа с помощью метода массива map и их количество сравнивается с количество аргументов конструктора выбранного класса. В итоге выбранный класс и аргументы используются для вычисления площади.

Запустим получившееся приложение и попробуем несколько примеров:

$ ruby area.rb

> circle 1

3.14159265358979

> circle 40.3

126.606183939669

> square 20

400.0

> unknown 30

?Unknown shape

 Вы можете продолжить знакомство с языком по материалам следующих сайтов:


Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru