Rubyn: различия между версиями

Содержимое удалено Содержимое добавлено
Нет описания правки
м орфография, викификатор
Строка 21:
 
== Отличия РубиНа от Ruby ==
Самое первое и главное отличие — при написании переменных, классов, методов Вы теперь не ограничены 128 первыми символами ANSI. Вам доступны все 256 8-битных символа. В данной реализации используется наиболее общеупотребительная кодировка Windows-1251.
 
Все ключевые слова, классы и медоты Ruby полностью доступны в Рубине, но кроме того все они имеют русскоязычные эквиваленты.
Самое первое и главное отличие - при написании переменных, классов, методов Вы теперь не ограничены 128 первыми символами ANSI. Вам доступны все 256 8-битных символа. В данной реализации используется наиболее общеупотребительная кодировка Windows-1251.
 
Все ключевые слова, классы и медоты Ruby полностью доступны в Рубине, но кроме того все они имеют русскоязычные эквиваленты.
Например: '''для ~ for'''
 
В Рубине в оригинальный язык Ruby добавлено одно ключевое слово '''equivalent ~ эквивалент''', которое по функциональной нагрузке весьма схоже с ключевым словом '''alias ~ дримя''' , но все же имеет существенное отличие.
 
В дальнейшем, если вы читаете что-либо о языке Ruby, то все это в полной мере относится и к языку РубиН. В случае отличий это всегда будет оговорено и будет использовано название РубиН.
 
== Основные свойства Ruby ==
 
Интерпретируемый язык:
* Возможность прямых [[w:системный вызов|системных вызовов]]
Строка 43 ⟶ 41 :
* Простой и последовательный синтаксис
* Автоматическое управление [[w:Оперативная память|оперативной памятью]].
Объектно-ориентированное программирование:
* Всё есть [[w:Экземпляр (программирование)|объект]]. Даже имя класса есть экземпляр класса <code>Class</code>
* [[w:Класс (программирование)|Классы]], [[w:Метод|методы]], [[w:Наследование (программирование)|наследование]], [[w:Полиморфизм в языках программирования|полиморфизм]], [[w:Инкапсуляция (программирование)|инкапсуляция]] и  т.  д.
* [[w:Одиночка (шаблон проектирования)|Методы-одиночки]]
* [[w:Примесь (программирование)|Примеси]] при помощи модулей (возможность расширить класс без наследования)
* Итераторы и [[w:Блок (программирование)|блоки]]
* Широкие возможности метапрограммирования
{|
|Удобства:
Строка 63 ⟶ 61 :
* Невозможность компиляции и сопутствующей ей [[w:Оптимизация (вычислительная техника)|оптимизации]] программы
** Открытость исходного кода даже в готовой программе <small>(есть [http://exerb.sourceforge.jp/index.en.html средство упаковки исходного кода в .exe-файл] под Windows)</small>
* Следствие двух первых недостатков  — весьма низкая скорость запуска и выполнения программ
|}
 
== Начало работы ==
 
От читателя требуется [[Информационные технологии|общее знание компьютеров]], включая [[Файловая система|навыки работы с файловой системой]] и [[Работа с электронным текстом|текстовыми файлами]]. Прежде чем начать программировать на Руби, нужно установить [[w:Интерпретатор|интерпретатор]] и обустроить для себя удобную [[w:Среда разработки программного обеспечения|среду для создания программ]]. Для испытания кода из учебника можно воспользоваться [http://tryruby.hobix.com/ сетевым интерактивным интерпретатором].
 
Строка 82 ⟶ 79 :
Также возможна настройка популярнейшей UNIX-оболочки '''mc''' таким образом, что вы сможете стартовать программу просто нажав Enter на имени программы.
 
Чтобы запустить программу из Windows нужно дважды кликнуть на файл с программой. Чтобы окно не исчезало и были видны результаты надо перед местами выхода из программы поставить команду ожидания ввода
<code>СТДВВ.ввс</code> или <code>СТДВВ.ввстр</code> или <code>STDIN.getc</code>
 
Строка 89 ⟶ 86 :
{{info|Именам программ в Windows принято давать расширение <tt>.rb'''w'''</tt> или <tt>.рб'''о'''</tt>, что позволяет использовать Windows-версию интерпретатора <tt>rubynw</tt>, который надоедающее окошко просто не вызывает. Интерпретатор <tt>rubyn</tt> предназначен для работы в консоли. Для интерпретатора <tt>rubynw</tt> вставлять код <code>СТДВВ.ввстр</code> ~ <code>STDIN.getc</code> перед местами выхода бесполезно}}
 
=== Первая программа ===
Откройте редактор и напишите программу. По традиции первая программа должна просто выводить приветствие миру:
Строка 102 ⟶ 100 :
 
=== Полигон для испытания программ ===
 
Чтобы посмотреть результат выполнения большинства примеров из учебника, не обязательно создавать файл. Достаточно использовать полигон для испытания программ на Руби <big>'''<code>irb</code>'''</big>, который входит во все дистрибутивы и сам написан на Руби. Запускается он командой
% <tt>irb</tt>
 
Полигон  — это рубиговорящая командная строка. Она показывает вам результат выполнения каждой строки кода:
% irb(main):001:0> <tt>[1,2,3,4]</tt>
% => [1, 2, 3, 4]
 
Поскольку любая функция в Ruby может быть переопределена в любой момент, важнейшая часть написания большинства небольших программ  — это именно эксперименты на полигоне. Посмотрим, например, какие есть методы у обычной строки:
% irb(main):002:0> <tt>"некий текст".методы</tt>
% => ["клон", "gem", "strip!", "член?", ...]
Строка 121 ⟶ 118 :
% => 70
 
Для выхода с полигона  — набрать <tt>выйти</tt> или <tt>exit</tt>.
 
{{Внимание|По причине того, что интерактивный терминал (полигон) поддерживает автозаполнение при введении табуляции, исходные тексты программ на Ruby принято «отбивать» пробелами (чтобы текст программы можно было просто скопировать в терминал и увидеть, как он работает). Если в <tt>irb</tt> скопировать программу, «отбитую» табуляциями, интерактивный терминал будет прерывать каждую строчку ввода чтобы предложить варианты автозаполнения.}}
Строка 128 ⟶ 125 :
 
==== fxri: полигон и справочник ====
 
[[Файл:Интерфейс программы fxri.png|300px|right|Внешний вид программы fxri]]
 
Строка 136 ⟶ 132 :
 
==== Полигон в сети ====
 
Иногда интерпретатора Руби нет под рукой, приходится использовать [http://tryruby.hobix.com/ сетевой полигон]. Он позволяет выполнять код на Руби прямо из браузера.
 
Строка 142 ⟶ 137 :
 
=== Комментарии и демонстрация результата ===
 
''Комментарием'' называется часть программного кода, пропускаемая при обработке (интерпретации или компиляции).
 
Строка 154 ⟶ 148 :
 
=== Вывод на экран ===
 
В Ruby есть много методов вывода: <code>print</code>, <code>printf</code>, <code>p</code>, <code>puts</code>, <code>.display</code> и другие. Но мы использовать будем два:
* метод <code>puts</code>. После вывода строки осуществляет переход на новую. Если приходится выводить объект, не являющийся строкой, то <code>puts</code> вызывает метод <code>.to_s</code> для преобразования его к строке;
Строка 178 ⟶ 171 :
 
=== Переменные ===
 
[[w:Переменная (программирование)|Переменные]] используются, чтобы сохранить промежуточный результат вычислений. Имя переменной в Ruby должно:
* начинаться со строчной буквы или знака подчеркивания;
Строка 246 ⟶ 238 :
=== Логический тип ===
<div style="float:right;margin:0.8em;padding:0.2em;background#fffcff;">
 
==== Логические операции ====
{| class="standard"
! название операции || символ операции || литерное обозначение
|- align=center |
| логическое "«или"» || <code><nowiki>||</nowiki></code> || or
|- align=center |
| логическое "«и"» || <code>&&</code> || and
|- align=center |
| логическое "«не"» || <code>!</code> || not
|- align=center
| логическое "«исключающее или"» || <code>^</code> || xor
|}</div>
 
[[w:Булевский тип|Логический (булев) тип]]  — это вариация на тему «да» или «нет». В Ruby он представлен двумя предопределенными переменными <code>true</code> («истина» или «да») и <code>false</code> («ложь» или «нет»). Появляется логический тип в результате логических операций или вызова логических методов.<br/>
 
Чаще всего логический тип возникает как результат сравнения.
 
==== Методы сравнения ====
{| class="standard"
! название метода || символ
|- align=center
Строка 288 ⟶ 281 :
 
=== Массивы ===
Разработчики Руби решили не реализовывать особых классов для динамических массивов, [[w:Линейный список|списков]], [[w:стек|стеков]] и тому подобного. Они все это реализовали в [[w:Индексный массив|массивах]] — структурах данных типа (или класса, — в Руби всё равно) <code>Array</code>. Сделано это путем добавления специальных методов; например, методы <code>.push</code> и <code>.pop</code> для стека. Особенности массивов в Руби:
 
Разработчики Руби решили не реализовывать особых классов для динамических массивов, [[w:Линейный список|списков]], [[w:стек|стеков]] и тому подобного. Они все это реализовали в [[w:Индексный массив|массивах]] — структурах данных типа (или класса, — в Руби всё равно) <code>Array</code>. Сделано это путем добавления специальных методов; например, методы <code>.push</code> и <code>.pop</code> для стека. Особенности массивов в Руби:
* Нет ограничений (это общий принцип языка). Массивы могут быть сколь угодно длинными.
* Динамичность: размер массива легко меняется.
Строка 306 ⟶ 298 :
maccuB=["Этот массив пойдёт в переменную maccuB", "Як-цуп-цоп, парви каридула"]</source>
 
'''Ползёт он всегда влево'''; на левом же конце его локомотив  — первый элемент. Первый потому, что элементы упорядочены. Если знаете порядковый номер элемента, то легко получить его значение:
<source lang="ruby">maccuB[1] #=> "Як-цуп-цоп, парви каридула"</source>
В мире поездов-гусениц счёт вагонов начинается с локомотива, а не со следующего за ним вагона. Таким образом локомотив  — это как бы нулевой вагон.
<source lang="ruby">maccuB[0] #=> "Этот массив пойдёт в переменную maccuB"</source>
 
Строка 314 ⟶ 306 :
 
=== Строки ===
[[w:Строковый тип|Строки]]  — это ряды букв и других символов. В Ruby строки используют наработки языка [[w:Перл|Перл]]. Вот небольшой список их возможностей:
* Нет ограничений. Длина строки может достигать поистине фантастических размеров.
* Динамичность. Строки можно расширять или уменьшать (для этого есть методы + и []).
Строка 329 ⟶ 321 :
 
=== Ассоциативные массивы ===
 
[[w:Ассоциативный массив|Ассоциативные массивы]] подобны массивам [[w:упорядоченная пара|упорядоченных пар]]. Работают они подобно словарям: фигурная скобка символизирует боковой вид на открытую книгу, а стрелка <code>=></code> покажет читателю связь каждого одного с чем-то другим. Вторая фигурная скобка говорит, что пора закрывать книгу.
<source lang=ruby>{ "мама" => "мыла раму", 807 => "Это число улыбается!"}</source>
Строка 344 ⟶ 335 :
 
=== Диапазоны значений ===
Чтобы было удобней получать подмассив или подстроку, был введен простенький тип данных — диапазон (класс <code>Range</code>). Диапазон формируется тремя элементами: начало, конец и тип протяженности (символ <code>..</code> или <code>...</code>). Начало и конец должны быть одного типа данных (одного класса) и быть '''перечислимыми''', что значит, иметь метод <code>.succ</code>. Пример диапазонов:
 
Чтобы было удобней получать подмассив или подстроку, был введен простенький тип данных — диапазон (класс <code>Range</code>). Диапазон формируется тремя элементами: начало, конец и тип протяженности (символ <code>..</code> или <code>...</code>). Начало и конец должны быть одного типа данных (одного класса) и быть '''перечислимыми''', что значит, иметь метод <code>.succ</code>. Пример диапазонов:
 
<source lang=ruby>'a'..'z'
Строка 352 ⟶ 342 :
1...100 # то же, что и 1..99</source>
 
Диапазон-мотоцикл (<big><code>..</code></big>) проезжает от первого указанного объекта к его <code>.succ</code> (''succedent''  — «последующему»), и до последнего включительно. Три точки  — то же, но мотоцикл остановился прямо перед последним элементом. Ещё раз:
 
<big><code>1..99</code>≡<code>1...100</code></big>
Строка 366 ⟶ 356 :
 
== Подробнее о числах ==
Изначально числа представлены тремя типами: два целых типа (классы <code>Fixnum</code> и <code>Bignum</code>) и один дробный (класс <code>Float</code>). Возможно подключение дополнительных типов,  — например, [[w:Комплексное число|комплексных]] и [[w:Рациональное число|рациональных]] чисел, но пока ограничимся тремя.
 
=== Целые числа ===
Целые числа в Ruby не ограничены по величине, то есть могут хранить сколь угодно большие значения. Для обеспечения такого волшебного свойства было создано два класса. Один из них хранит числа меньшие <math>2^{31}</math> (по модулю), а второй  — всё, что больше. По сути, для больших чисел создается массив из маленьких, а раз массив не имеет ограничений по длине, то и число получается неограниченным по значению.
 
Как только число типа <code>Fixnum</code> становится больше <math>2^{31}</math> (по модулю), то оно преобразовывается к классу Bignum. Если число типа Bignum становится меньше <math>2^{31}</math>, то оно преобразовывается к типу <code>Fixnum</code>.
 
При записи целых чисел сначала указывается знак числа (знак <code>+</code> обычно не пишется). Далее идет основание [[w:система счисления|системы счисления]], в которой задается число (если оно отлично от [[w:десятичная система счисления|десятичной]]): <code>0</code>  — для [[w:восьмеричная система счисления|восьмеричной]], <code>0x</code>  — для [[w:шестнадцатеричная система счисления|шестнадцатеричной]], <code>0b</code>  — для [[w:двоичная система счисления|двоичной]]. Затем идет последовательность цифр, выражающих число в данной системе счисления. При записи чисел можно использовать символ подчеркивания, который игнорируется при обработке. Чтобы закрепить вышесказанное, посмотрим примеры целых чисел:
<source lang=ruby># тип Fixnum
123_456 # подчеркивание игнорируется
Строка 404 ⟶ 394 :
В отличие от большинства элементарных типов данных, числа обладают своей иерархией. Все числа в Руби наследованы от класса Numeric (Числовой). Поэтому, если хотите добавить новый метод ко всем числам, то нужно расширять именно этот класс. Далее идет деление чисел: Integer ([[w:Целое число|целое]]), Float ([[w:Десятичная дробь|дробное]]) и Complex ([[w:Комплексное число|комлексное]]). При желании можно добавить и Rational ([[w:Рациональное число|рациональное]]), но на данном семейном портрете оно отсутствует.
 
От класса Integer наследуются <big>два</big> класса: Fixnum (целое маленькое) и Bignum (целое большое). К первому относятся все числа, [[w:Абсолютная величина|по модулю]] меньшие <math>2^{31}</math> , а ко второму  — все остальные.
 
{{Внимание|
Строка 413 ⟶ 403 :
 
=== Арифметические операции ===
Арифметические операции в Ruby обычны: [[w:Сложение|сложение]] (<code>+</code>), [[w:Вычитание|вычитание]] (<code>-</code>), [[w:Умножение|умножение]] (<code>*</code>), [[w:Деление (математика)|деление]] (<code>/</code>), получение остатка от деления (<code>%</code>), возведение в степень (<code>**</code>).
 
Арифметические операции в Ruby обычны: [[w:Сложение|сложение]] (<code>+</code>), [[w:Вычитание|вычитание]] (<code>-</code>), [[w:Умножение|умножение]] (<code>*</code>), [[w:Деление (математика)|деление]] (<code>/</code>), получение остатка от деления (<code>%</code>), возведение в степень (<code>**</code>).
<source lang=ruby>6 + 4 #-> 10
6 - 4 #-> 2
Строка 422 ⟶ 411 :
6 ** 4 #-> 1296</source>
 
Эти операции используются как дробными, так и целыми числами (а также рациональными дробями и комплексными).
 
Порядок вычисления обычный, как учили в школе. Для изменения приоритета применяются круглые скобки:
Строка 428 ⟶ 417 :
(2 + 2) * 2 #-> 8</source>
 
Первое, что бросается в глаза  — результат арифметической операции двух целых чисел всегда будет целым. Особенно это видно при делении:
<source lang=ruby>1/3 #-> 0
2/3 #-> 0
Строка 435 ⟶ 424 :
{{Рамка}}Если все аргументы арифметического выражения целые числа, то результат будет целым, если хотя бы одно дробное, то результат будет дробным.
{{Начало цитаты}}
Одна вторая в Ruby ноль,<br />
А три вторые  — единица.<br />
Запомнить надо эту соль,<br />
Чтоб результату не дивиться.
{{Конец цитаты}}
{{Акмар}}
 
Посмотрим, каковы результаты, когда одно из чисел дробное.
<source lang=ruby>6.0 + 4 #-> 10.0
6 - 4.0 #-> 2.0
Строка 487 ⟶ 476 :
 
==== Операции с присвоением ====
 
Часто можно встретить выражения вида:
<source lang=ruby>ogHo_4ucJIo += gpyroe_4ucJIo</source>
Строка 512 ⟶ 500 :
! Метод || Операция
|-
| <code>to_f</code> || преобразовать в дробное
|-
| <code>to_i</code> || преобразовать в целое
|-
| <code>to_s</code> || преобразовать в строку
|-
| <code>to_a</code> || преобразовать в массив
|}
 
Методы преобразования типов в Ruby традиционно начинаются с приставки <code>to_</code>. Последующая буква  — это сокращение от названия класса, в который происходит преобразование (<code>f</code>  — <code>Float</code>  — дробное, <code>i</code>  — <code>Integer</code>  — целое, <code>s</code>  — <code>String</code>  — строка, <code>a</code>  — <code>Array</code>  — массив). Посмотрим их действие на примере:<source lang=ruby>7.to_f #-> 7.0
7.9.to_i #-> 7
7.to_s #-> "7"
Строка 531 ⟶ 519 :
 
В первом случае, метод <code>rand</code> возвращает <u>целое число</u> в диапазоне от 0 до 99 (на 1 меньше 100). Во втором случае, метод <code>rand</code> возвращает <u>дробное число</u> в диапазоне от 0.0 до 1.0 включительно. Различие в результате обусловлено передаваемым параметром:
* если передается параметр (в данном случае 100), то генерируется <u>целое</u> случайное число (в диапазоне <code>0...N-1</code>, где <code>N</code>  — передаваемый аргумент);
* если параметр отсутствует, то генерируется <u>дробное</u> число в диапазоне от 0.0 до 1.0.
 
Строка 537 ⟶ 525 :
Array.new(5){ rand(100) } #-> [69, 71, 28, 42, 22]</source>
 
Если вы выполните данную программу у себя дома, то получите тот же самый массив. 123  — номер «случайной» последовательности. Измените его и массив изменится!
 
Если вызвать <code>srand</code> без параметра или не вызывать его вообще, то номер «случайной» последовательности выбирается случайным образом.
 
=== Хитрости ===
 
Задача: выдать целое число в [[w:двоичная система счисления|двоичной системе счисления]].
<source lang=ruby>ucxogHoe_4ucJIo = 1234
Строка 577 ⟶ 564 :
 
== Подробнее о массивах ==
Массивы — это тип данных, с которым вам придется работать постоянно. Облик большинства программ зависит именно от правильного (читай «изящного») использования массивов.
 
Массивы — это тип данных, с которым вам придется работать постоянно. Облик большинства программ зависит именно от правильного (читай «изящного») использования массивов.
 
=== Способы создания массива ===
 
Массив создается как минимум тремя способами. Первый способ:
<source lang=ruby>[1,2,3,4,5,6]</source>
Строка 594 ⟶ 579 :
 
=== Диапазоны ===
 
Методом <code>to_a</code> очень удобно создавать из диапазона массив, содержащий упорядоченные элементы данного диапазона.
<source lang=ruby>(1..10).to_a #-> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Строка 605 ⟶ 589 :
<code>[ 'a'<math>{}^{0}</math>, 'b'<math>{}^{1}</math>, 'c'<math>{}^{2}</math>, 'd'<math>{}^{3}</math>, 'e'<math>{}^{4}</math> ]</code>
 
Такая нумерация называется в Ruby положительной индексацией. Хм,  — скажете вы,  — а есть еще и отрицательная? Да, есть!
<code>[ 'a'<math>{}^{+0}_{-5}</math>, 'b'<math>{}^{+1}_{-4}</math>, 'c'<math>{}^{+2}_{-3}</math>, 'd'<math>{}^{+3}_{-2}</math>, 'e'<math>{}^{+4}_{-1}</math> ]</code>
 
Строка 618 ⟶ 602 :
maccuB[ -2 ] #-> 'd'</code>
 
Индекс -2−2 значит «второй с конца элемент массива». Вот так и появилась отрицательная индексация. Теперь давайте разберемся с диапазонами. Оказывается, в них тоже можно использовать отрицательную индексацию. Вот как можно получить все элементы массива кроме первого и последнего:
 
<code>maccuB = [ 'a', <u>'b', 'c', 'd'</u>, 'e']
Строка 631 ⟶ 615 :
 
=== О двумерных массивах ===
Для Ruby '''двумерный массив''' — ''это не более чем массив, содержащий одномерные массивы''. Вот несколько примеров двумерных массивов:
 
Для Ruby '''двумерный массив''' — ''это не более чем массив, содержащий одномерные массивы''. Вот несколько примеров двумерных массивов:
 
<source lang=ruby>[[1],[2,3],[4]] # разная длина элементов-массивов
Строка 646 ⟶ 629 :
 
=== Методы работы с массивами ===
 
Разнообразие и полезность методов у массивов создаёт впечатление, что все сложные алгоритмы уже реализованы. Это не так, программистам Руби дана действительно обширная библиотека методов. Здесь мы рассмотрим лишь самые употребимые; остальные ищите в справочнике.
 
==== Получение размера массива ====
 
В Ruby массивы динамические: в каждый конкретный момент времени неизвестно сколько в нем элементов. Чтобы не плодить тайн подобного рода и был реализован метод <code>.size</code>:
 
Строка 664 ⟶ 645 :
 
==== Поиск максимального/минимального элемента ====
 
Вспомните сколько усилий вам приходилось прилагать, чтобы найти максимальный элемент? А сколько раз вы повторяли этот кусок кода в своих программах? Ну а в Ruby поиск максимального элемента осуществляется при помощи метода <code>.max</code>, а в более сложных случаях при помощи метода <code>.max_by</code> (начиная с версии 1.9). Вот как это выглядит:
 
Строка 678 ⟶ 658 :
 
==== Упорядочение ====
 
Не буду травить душу долгими байками. Чтобы упорядочить массив, нужно вызвать метод <code>.sort</code> или <code>.sort_by</code> (начиная с версии 1.8).
 
Строка 689 ⟶ 668 :
 
==== Обращение массива ====
{{Рамка}}'''Обращение массива''' — это изменение порядка элементов на обратный, то есть первый элемент становится последним, второй элемент предпоследним и так далее.{{Акмар}}
 
{{Рамка}}'''Обращение массива''' — это изменение порядка элементов на обратный, то есть первый элемент становится последним, второй элемент предпоследним и так далее.{{Акмар}}
 
Для обращения массива существует метод <code>.reverse</code>. Применим его к предыдущим примерам, чтобы получить сортировку по убыванию:
Строка 700 ⟶ 678 :
 
==== Сложение/вычитание массивов ====
 
Для сложения массивов, строк и чисел используется метод <code>+</code>:
 
Строка 714 ⟶ 691 :
 
==== Объединение и пересечение массивов (как множеств) ====
 
Очень часто приходится решать задачи, в которых нужно оперировать [[w:множество|множествами]]. У массивов припасено для этих целей два метода: <code>|</code> ([[w:объединение множеств|объединение]]) и <code>&</code> ([[w:пересечение множеств|пересечение]]).
 
Строка 728 ⟶ 704 :
<code>[1,2,3,4,5,<s>5</s>,6,0,<s>1</s>,<s>2</s>,<s>3</s>,<s>4</s>,<s>5</s>,7]</code>
 
Зачеркнутые числа  — это удаленные элементы (дубликаты). Переходим от слов к делу:
<code>[1,2,3,4,5,5,6] & [0,2,1,3,5,4,7] #-> [1,2,3,4,5]</code>
 
Строка 739 ⟶ 715 :
 
==== Удаление дубликатов ====
 
Для удаления дубликатов (повторяющихся элементов массива) в Ruby используется метод <code>.uniq</code>:
<code>[1,2,3,4,5,5,6,0,1,2,3,4,5,7]<u>.uniq</u> #-> [1,2,3,4,5,6,0,7]</code>
Строка 766 ⟶ 741 :
 
==== Удаление неопределенных(nil) элементов ====
 
Функцию удаления nil элементов массива выполняет метод <code>.compact</code> например:
 
Строка 773 ⟶ 747 :
 
==== Транспонирование двумерного массива ====
Задача: дан двумерный массив. Вывести одномерный массив с максимумами каждого из столбцов. Хм… посмотрим сперва, как эта задача решается для строчек, а не столбцов:
 
Задача: дан двумерный массив. Вывести одномерный массив с максимумами каждого из столбцов. Хм... посмотрим сперва, как эта задача решается для строчек, а не столбцов:
<source lang=ruby>maccuB_2D = [[1,2],[3,4]]
maccuB_2D.map{ |maccuB| maccuB.max } #-> [2,4]</source>
Строка 785 ⟶ 758 :
 
==== Размножение массивов ====
 
Речь пойдет не о почковании, а о методе, который позволяет умножать массив на целое число. В результате такого умножения мы получим массив, состоящий из нескольких копий элементов исходного массива.
 
Строка 799 ⟶ 771 :
 
==== Функциональность [[w:Стек|стека]] ====
 
Часто и во многих алгоритмах надо добавить элемент в конец массива:
 
Строка 820 ⟶ 791 :
 
==== Функциональность очереди и списка ====
 
Чтобы можно было использовать массив в качестве [[w:Очередь|очереди]] и/или [[w:Линейный список|списка]], потребуется сделать всего лишь пару методов. Первый из них добавляет элемент в начало массива, а второй удаляет элемент из начала. Давайте посмотрим, как это делается универсальными методами <code>[]</code>, <code>[]=</code> и <code>+</code>:
 
Строка 845 ⟶ 815 :
maccuB #-> [1,2,3,4,5]</code>
 
Удаляющий метод  — <code>.shift</code> («сдвинуть»), а метод, добавляющий элемент в начало массива, называется <code>.unshift</code> (непереводимое слово, стремящееся означать нечто противоположное слову «сдвинуть»).
 
==== Мнемограмма для методов стека/очереди/списка ====
 
Мнемограмма для методов <code>.shift</code>, <code>.unshift</code>, <code>.pop</code> и <code>.push</code>:
 
Строка 855 ⟶ 824 :
<code>.shift</code> <code>.pop</code>
 
Методы с параметром (сверху) добавляют элемент в массив, а методы без параметра (снизу)  — удаляют. По желанию можно дорисовать стрелочки.
 
{{Начало цитаты}}
Метод <code>.shift</code> сдвигает влево,<br />
Метод <code>.pop</code>  — направо.<br />
Метод <code>.push</code> к концу цепляет,<br />
А <code>.unshift</code>  — к началу.
{{Конец цитаты}}
 
<small>Замечание. Имена методов unshift/shift неинтуитивны. Во-первых, они не напоминают о том, что работа идет с головой массива, а не с хвостом, во-вторых ничего не говорят о том, идет заполнение или опустошение стека. Можно создать для этих методов псевдонимы с говорящими именами, например, feed/spit (кормить/выплевывать):<br />
<source lang="ruby">
class Array
Строка 879 ⟶ 848 :
 
Где это может понадобиться? Например, вы реализуете класс, который умеет читать из файла записи определенной структуры.
Основную его логику занимает именно чтение нужного формата, кеширование, разбор, десериализация и  т. п.
 
Просто реализуйте <code>.each</code> и включите в ваш класс mixin <code>Enumerable</code>. В нем находится реализация
методов, таких как <code>.inject</code>, <code>.each_with_index</code> и  т. п.
 
=== Логические методы ===
{{Рамка}}'''Логический метод''' — это метод, результатом которого является логическое выражение (true или false).{{Акмар}}
 
{{Рамка}}'''Логический метод''' — это метод, результатом которого является логическое выражение (true или false).{{Акмар}}
 
По японской традиции, имена логических методов принято заканчивать <code>?</code> (вопросительным знаком). Это позволяет также получить список логических методов, вызываемых в данном случае: просто отобрать из всех имеющихся методов те, что кончаются на <code>?</code>. Делается это при помощи небольшой вспомогательной программы:
Строка 897 ⟶ 865 :
 
==== Есть элемент в массиве? ====
 
Как узнать, есть ли некоторый элемент в массиве? Попробуем решить эту задачу при помощи метода <code>.size</code> и итератора <code>.find_all</code>:
<source lang=ruby>maccuB = [1,2,3,4,5,6,7]
Строка 904 ⟶ 871 :
# это значит, что такое число есть</source>
 
Использование связки из трех методов (<code>!=</code>, <code>.find_all</code> и <code>.size</code>) для такой задачи  — возмутительно! Разработчики не могли с этим долго мириться и реализовали метод специально для этой задачи. Имя ему  — <code>.include?</code>. Перепишем нашу задачу, но на этот раз будем использовать правильный метод:
 
<code>maccuB = [1,2,3,4,5,6,7]
Строка 912 ⟶ 879 :
 
{{Начало цитаты}}
Мутный горизонт скрывает берег,<br />
Ветер мокр, холоден и лют.<br />
Есть ли в озере акулы, я проверю<br />
Методом логическим <code>.include?</code>
{{Конец цитаты}}
Строка 924 ⟶ 891 :
 
==== Массив пустой? ====
 
Если вы хотите задать массиву вопрос «ты пуст ли?», но боитесь обидеть, то можете пойти окружным путем. Например, спросить у него: ты равен пустому массиву?
 
Строка 949 ⟶ 915 :
noJIHbIu_maccuB<u>.empty?</u> #-> false</code>
 
<!-- Не согласен!
Выражение not maccuB.empty? выглядит более понятным, чем maccuB.any?
А если хочется короче, то написать !maccuB.empty? тоже труда не составляет.
Строка 956 ⟶ 922 :
 
==== И наоборот ====
В Руби принято избегать отрицания условия. Например, если вам нужно сделать что-то, если массив '''не пуст''', можно
 
В Руби принято избегать отрицания условия. Например, если вам нужно сделать что-то, если массив <b>не пуст</b>, можно
воспользоваться методом, обратным <tt>empty?</tt>. Этот метод называется <tt>any?</tt>
 
Строка 968 ⟶ 933 :
 
=== Итераторы ===
Массивы  — эти эшелоны переменных, эти ожерелья запятых и элементов,  — часто приходится проходить целиком, обследуя каждый элемент. Да и не только массивы, но и любые последовательности чего-нибудь.
 
В старину люди делали это [[w:цикл (программирование)|циклами]]. В Руби у списочных структур данных есть встроенные методы, которые проходят весь ряд поэлементно, но, в отличие от циклов:
* не зацикливаются: счётчик цикла нельзя докручивать;
* выполняются заведомое число раз;
* их много, и каждый делает своё дело.
<center><big>Имя им  — <big>'''''итераторы'''''</big></big>.</center>
 
==== Изменение всех элементов массива ====
 
Изменить все элементы массива можно по-всякому. Начнём с обнуления:
 
Строка 985 ⟶ 949 :
{{Внимание|В приведенном примере каждый элемент массива будет заменен нулем независимо от того, чем является этот элемент. Например, при попытке обнулить таким образом двумерный массив <code>[[1, 2], [3, 4]]</code>, в результате получим <code>[0, 0]</code>.}}
 
Используется '''итератор''' <code>.map</code>, за которым следует <big>'''''блок'''''</big>,  — кусочек кода, схваченный лапками-фигурными скобками. <code>.map</code> последовательно проходит <code>maccuB</code> и выполняет блок заново для каждого элемента. То, что выходит из блока, итератор <code>.map</code> делает очередным элементом нового массива.
 
Можно дать элементу <code>.map</code> иное задание. Для этого зажимаем в фигурные скобы блока иной код:
Строка 993 ⟶ 957 :
 
Прежде, чем блоку выдать квадрат очередного элемента, ему нужно знать этот элемент. Итератор <code>.map</code> даёт ему значение элемента, словно фотографию, обрамлённую слева и справа вертикальными чертами <code>|</code>. Чтобы блок смог взять эту фотографию, обязательно дать ей имя. В нашем случае это <code>elem</code>, но подходят и такие названия:
* <code>_3JleMeHt</code>
* <code>x</code>
* <code>y</code>
Строка 1003 ⟶ 967 :
Но ещё важно помнить, что элементы не уходят из первого вагона: блок лишь осматривает каждый элемент, берёт его значение, но не меняет. Всё, что получается в результате работы блока, садится в очередной вагон ''другого'' поезда.
 
То имя, что показывается в раздвижных дверях,  — это не сам элемент, это лишь его копия. Фотография. Голограмма. Это даже не другая переменная, это не переменная вообще. Бессмысленно присваивать новое значение фотографии:
 
<code>maccuB = [1,2,3,4,5]
Строка 1020 ⟶ 984 :
maccuB #-> [1,4,9,16,25]</code>
 
Это общее явление Руби: методы (здесь  — итераторы) не меняют объект (массив), с которым работают. Они лишь выдают результат, который потом можно использовать как аргумент или присвоить переменной.
 
Явление было воспето в фольклоре:
 
{{Начало цитаты}}
Метод <code>.map</code> все изменяет,<br />
Как кто пожелает<br />
И обижается на тех,<br />
Кто результат не сохраняет.
{{Конец цитаты}}
 
==== Отбор элементов по признаку ====
 
Вот как итератор <code>.find_all</code> выберет из массива все чётные элементы:
 
Строка 1038 ⟶ 1001 :
maccuB.find_all{ |elem| <u>elem%2==0</u> } #-> [2,4]</code>
 
<code>elem%2==0</code>  — это вопрос "«чётен ли <code>elem</code>?"». Ответом, как всегда, будет <code>true</code> или <code>false</code>. Ведь "«чётность" » — это равенство нулю (<code>==0</code>) остатка деления (<code>%</code>) на 2.
 
Кстати, равенство нулю можно проверять и при помощи метода <code>.zero?</code>. А чётность тоже можно проверить разными способами:
Строка 1064 ⟶ 1027 :
 
{{Начало цитаты}}
Если нужно элементы<br />
По условию искать,<br />
То полезнее <code>.find_all</code><br />
Метода вам не сыскать!
{{Конец цитаты}}
 
 
<small>Мысль: красивее делить лишь последнюю цифру(так в третьем классе учат четность определять :) ). Тем более, когда нужно определить четность factorial(100) :).
В любом случае базовый класс нужно расширить чем-то вроде метода <code>.chetnoe?</code>.(с)Вовчик
</small>
Строка 1077 ⟶ 1040 :
<small>Ошибка: <code>(elem&1).zero?</code> проверит на нечетность. Для проверки на четность надо <code>!(elem&1).zero?</code></small>
 
<small>Ошибка комментатора: как раз наоборот. Разберем: <code>(elem&1)</code> - — битовая операция, вернет первый бит числа (составляющую 2<sup>0</sup> ). То есть результатом будет 0, если elem - — число четное, и 1 в ином случае.
<code>0.zero? #true</code>
<code>1.zero? #false</code>
Строка 1083 ⟶ 1046 :
 
==== Суммирование/произведение/агрегация элементов ====
 
Очень часто возникает задача найти сумму/произведение всех элементов массива. Для этих целей (начиная с версии 1.8) традиционно используется итератор <code>.inject</code>. Для демонстрации его работы, давайте найдем сумму элементов массива:
 
Строка 1089 ⟶ 1051 :
maccuB.inject( <u>0</u> ){ |<u>result, elem</u>| <u>result + elem</u> } #-> 15</code>
 
Рассмотрим все по порядку. Начнем с нуля. Его следует расшифровывать как '''<code>result = 0</code>''' перед началом работы итератора, то есть это начальное значение переменной <code>result</code> (переменной промежуточного результата).
 
Далее идет объявление двух переменных. Первая из них (<code>result</code>) будет хранить промежуточный результат. Вторая (<code>elem</code>)  — фотография текущего элемента массива (или последовательности), мы такую уже видели.
 
После объявления описан алгоритм работы итератора. В данном случае ему предписано каждый элемент массива складывать с промежуточной суммой: <code>result + elem</code>.
Строка 1105 ⟶ 1067 :
{{info|Имена переменных <code>result</code> и <code>elem</code> созданы богатым воображением автора. В ваших программах они могут называться иначе.}}
 
Изменим имена переменных:
 
<code>maccuB = [1,2,3,4,5]
Строка 1117 ⟶ 1079 :
maccuB.inject( <u>1</u> ){ |pe3yJibTaT, nepemeHHa9| pe3yJibTaT <u>*</u> nepemeHHa9 } #-> 120</code>
 
Чтобы закрепить материал, решите задачу: найти произведение всех положительных элементов массива. Подсказка: используйте метод <code>.find_all</code>.
 
{{Начало цитаты}}
Элементов надо кучу<br />
Перемножить иль сложить? <br />
Есть <code>.inject</code> на этот случай,<br />
Его бы вам употребить.
{{Конец цитаты}}
 
==== Разбиение надвое ====
 
Итератор <code>.partition</code> делит массив на две части по некоторому бинарному признаку (чётности, положительности, наличию высшего образования и тому подобным). Вот как разделить массив на две части по признаку кратности трём:
 
Строка 1147 ⟶ 1108 :
 
=== Логические итераторы ===
 
В версии 1.8 появилось несколько логических методов: <code>.all?</code> и <code>.any?</code>. Они положили начало такому классу методов, как логические итераторы.
 
{{Рамка}}'''Логический итератор'''  — это итератор (метод, обрабатывающий все элементы последовательности), возвращающий значение логического типа  — <code>true</code> или <code>false</code>.{{Акмар}}
 
Конечно же, идея логических итераторов долгое время летала в ноосфере. Существовали итераторы, которые являлись условно-логическими: они возвращали <code>nil</code> в случае неудачи и какой-либо объект  — в случае удачи. В логическом [[w:Контекст|контексте]] поведение таких итераторов можно было посчитать логическим (<code>false</code> -> <code>nil</code>, а <code>true</code> -> число/строка/любой_объект). Примером условно-логического итератора служит метод <code>.detect</code>.
 
==== Все ли элементы удовлетворяют условию? ====
 
В [[w:Математическая логика|математической логике]] такой «итератор» называется [[w:Квантор|квантором]] общности, обозначается символом <math>\forall</math>. На языке Ruby он называется <code>.all?</code>. По сложившейся традиции, давайте посмотрим, как решалась бы эта задача до версии 1.8, то есть до появления логических итераторов:
 
Строка 1173 ⟶ 1132 :
 
==== Хотя бы один элемент удовлетворяет условию? ====
Вслед за [[w:Квантор|квантором]] общности (он же — логический итератор <code>.all?</code>), из [[w:Математическая логика|математической логики]] был перенесен и квантор существования — <math>\exists</math>. На языке Ruby он называется <code>.any?</code>. Чтобы оценить его по достоинству, посмотрим решение задачи без его участия. Проверим, содержит ли <code>maccuB</code> хотя бы один элемент больший двух:
 
Вслед за [[w:Квантор|квантором]] общности (он же — логический итератор <code>.all?</code>), из [[w:Математическая логика|математической логики]] был перенесен и квантор существования — <math>\exists</math>. На языке Ruby он называется <code>.any?</code>. Чтобы оценить его по достоинству, посмотрим решение задачи без его участия. Проверим, содержит ли <code>maccuB</code> хотя бы один элемент больший двух:
 
<code>maccuB = [1,2,2,3]
Строка 1191 ⟶ 1149 :
 
=== Хитрости ===
Вот так можно сгенерировать «хороший пароль»  — произвольную последовательность из чисел или латинских букв, общей длиной в 8 символов.
<source lang=ruby>cumBoJIbI = ['a'..'z','A'..'Z','0'..'9'].map{ |range| range.to_a }.flatten
puts (0...8).map{ cumBoJIbI[ rand( cumBoJIbI.size ) ] }.join</source>
Строка 1203 ⟶ 1161 :
=== Задачи про массивы ===
{{info|Помимо нижеследующих задач, советуем вам взглянуть на задачи из нашего [[/Задачник|сборника задач]]}}
 
==== Одномерные ====
 
# Вывести индексы массива в том порядке, в котором соответствующие им элементы образуют возрастающую последовательность.
# В численном массиве найти сумму отрицательных элементов.
# Найти все индексы, по которым располагается максимальный элемент.
# В массиве переставить в начало элементы, стоящие на четной позиции, а в конец -- — стоящие на нечётной.
 
===== Одномерные целочисленные =====
 
# Найти все элементы, большие среднего арифметического элементов.
# К четным элементам прибавить первый элемент, а к нечетным  — последний. Первый и последний элемент не изменять.
# Заменить все положительные элементы на значение минимального.
# Найти произведение всех четных элементов массива.
Строка 1221 ⟶ 1178 :
 
==== Двумерные ====
 
# Поменять первый и последний столбец массива местами.
# Упорядочить N-ый столбец.
Строка 1232 ⟶ 1188 :
 
===== Двумерные целочисленные =====
 
# Найти максимальный элемент для каждого столбца, а затем получить произведение этих элементов.
# Найти минимум в двумерном массиве.
Строка 1238 ⟶ 1193 :
# Найти сумму положительных элементов, больших К.
# Вычислить сумму и среднее арифметическое элементов главной диагонали.
# Найти номера строк, все элементы которых  — нули.
 
== Подробнее об ассоциативных массивах ==
Различают два типа массивов: индексные, у которых в качестве индекса только целое число и ассоциативные, где индексом может быть любой объект.
 
Различают два типа массивов: индексные, у которых в качестве индекса только целое число и ассоциативные, где индексом может быть любой объект.
 
{{info|Индексные массивы чаще всего называют просто «массивами», а ассоциативные массивы — «хешами» или «словарями».}}
Строка 1266 ⟶ 1220 :
{{Рамка}}'''Первый случай применимости хеша''': если в массиве намечаются обширные незаполненные (то есть заполненные <code>nil</code>) области, то целесообразнее использовать хеш с целочисленным индексом.{{Акмар}}
 
Использовать хеш в данном случае лучше потому, что, формально, хеш для данного примера состоит из трех значащих пар, а массив  — из шести элементов, из которых лишь три элемента значащие. Исходя из этого, можно заключить, что массив будет хранить избыточную информацию, а хеш  — только нужную.
 
Продолжим поиски случаев применимости хеша и, на этот раз подсчитаем, сколько раз каждое число повторяется в данном целочисленном массиве. Решение массивом:
Строка 1274 ⟶ 1228 :
#-> [[1, 3], [2, 4], [3, 1], [4, 1], [5, 1]]</code>
Алгоритм получается ужасным. Не буду утомлять излишними терминами, а замечу, что по одному и тому же массиву итераторы (в количестве двух штук) пробегают много раз. А ведь достаточно одной "«пробежки"». Понятное дело, что такая программа не сделает вам чести. В качестве упражнения, предалагаю вам решить эту задачу другим, более оптимальным, способом.
 
Теперь рассмотрим решение этой же задачи, но с применением хеша:
Строка 1284 ⟶ 1238 :
} #-> {5=>1, 1=>3, 2=>4, 3=>1, 4=>1}</code>
Удалось избавиться от лишних методов и обойтись лишь одной "«пробежкой"» итератора по массиву.
* Начальный хеш был создан хитроумной комбинацией <code>Hash.new{ 0 }</code>, что в переводе на русский означает примерно следующее: "«создадим пустой хеш, в котором любому несуществующему ключу будет соответствовать <code>0</code>"». Это нужно, чтобы суммирование (метод <code>+</code>) не выдавало ошибку вида: "«не могу сложить <code>nil</code> и число типа Fixnum"». В качестве упражнения, предлагаю вам заменить комбинацию <code>Hash.new{ 0 }</code> на <code>{}</code> и посмотреть, чем это чревато.
* зачем нужно дописывать <code>result</code>? Дело в том, что комбинация <code>result[i]+=i</code> имеет в качестве результата целое число (учитывая, что массив целочисленный), а не хеш. Следовательно, параметру <code>result</code> автоматически будет присвоено целое число (см. описание итератора <code>.inject</code>). На следующей итерации мы будем обращаться к <code>result</code>, как к хешу, хотя там уже будет храниться число. Хорошо, если программа выдаст ошибку, а если нет? Проверьте это самостоятельно.
 
Строка 1304 ⟶ 1258 :
 
В данном примере было использовано два интересных приема:
* Если в двумерном массиве заранее известное количество столбцов (в нашем случае  — два), то каждому из столбцов (в рамках любого итератора) можно дать свое имя (в нашем случае: <code>key</code> и <code>value</code>). Если бы мы такого имени не давали, то вышеописанное решение выглядело бы так:
 
<code>maccuB.find_all{ |<u>array</u>| <u>array[0]</u> == dns_name }<u>[0][-1]</u>
Строка 1311 ⟶ 1265 :
{{Внимание|Без именования столбцов, внутри итератора вы будете работать с массивом (в двумерном массиве каждый элемент — массив, а любой итератор "пробегает" массив поэлементно). Это высказывание действительно, когда "пробежка" осуществляется по двумерному массиву.}}
 
* метод <code>.find_all</code> возвращает двумерный массив примерно следующего вида: <code>[ ["comp1.mydomen.ru", "192.168.0.3"] ]</code>, чтобы получить строку <code>"192.168.0.3"</code> необходимо избавиться от двумерности. Делается это при помощи метода <code>[]</code>, который вызывается два раза (понижает размерность c двух до нуля). Метод <code>[0]</code> возвращает в результате  — <code>["comp1.mydomen.ru", "192.168.0.3"]</code>, а метод <code>[-1]</code>  — <code>"192.168.0.3"</code>. Как раз это нам и было нужно.
 
Теперь ту же самую задачу решим, используя хеш:
Строка 1319 ⟶ 1273 :
xew[ 'comp1.mydomen.ru' ] #-> "192.168.0.3"</code>
 
Нет ни одного итератора и следовательно, не сделано ни одной "«пробежки"» по массиву.
 
{{Рамка}}'''Третий случай применимости хеша''': когда требуется сопоставить один набор данных с другим, то целесообразнее использовать хеш.{{Акмар}}
 
Вполне естественно, что существуют и другие "«случаи применимости хеша"», но вероятность столкнуться с ними в реальной работе намного меньше. Вышеописанных трех "«случаев"» должно хватить надолго.
 
В заключении, как и было обещанно, приводится решение задачи с использованием метода <code>.update</code>:
Строка 1334 ⟶ 1288 :
 
=== Что используется в качестве ключей? ===
В качестве ключей ассоциативного массива можно использовать любые типы. Например, другие ассоциативные массивы, строки, числа, символы или просто обьекты любых классов.
 
В качестве ключей ассоциативного массива можно использовать любые типы. Например, другие ассоциативные массивы, строки, числа, символы или просто обьекты любых классов.
 
{{Внимание|Если состояние объектов-ключей изменилось, то хешу необходимо вызвать метод <code>.rehash</code>.}}
Строка 1348 ⟶ 1301 :
xew[ maccuB_1 ] #-> 100</code>
 
В данном примере ключами хеша (<code>xew</code>) являются два массива (<code>maccuB_1</code> и <code>maccuB_2</code>). Одному из них (<code>maccuB_1</code>) мы изменили нулевой элемент (с "«а"» на "«я"»). После этого доступ к значению был потерян. После выполнения метода <code>.rehash</code> все встало на свои места.
 
{{info|''Как Руби отслеживает изменение ключа в ассоциативном массиве? Очень просто: с помощью метода <code>.hash</code>, который генерирует "контрольную сумму" обьекта в виде целого числа. Например: <code>[1,2,3].hash #-> 25</code>''}}
 
=== Способы создания ассоциативного массива ===
 
При создании ассоциативного массива важно ответить на несколько вопросов:
* Какие данные имеются?
Строка 1362 ⟶ 1314 :
 
==== Из одномерного массива ====
 
Положим, что ''у нас в наличии индексный массив, где ключ и значение записаны последовательно''. Тогда мы можем использовать связку методов <code>*</code> и <code>Hash[]</code>:
 
Строка 1371 ⟶ 1322 :
 
==== Из двумерного массива ====
 
Если повезло меньше и нам достался двумерный массив с элементами вида <code>[["ключ_1", "значение_1"], ["ключ_2", "значение_2"], ["ключ_3", "значение_3"], ... ]</code>, то его надо сплющить (<code>.flatten</code>) и тем задача будет сведена к предыдущей:
<code>maccuB = [[1,4],[5,3],[2,2]]
Hash[ *maccuB<u>.flatten</u> ]</code> #-> {5=>3, 1=>4, 2=>2}
 
Каждый нулевой элемент подмассива станет ключом, а каждый первый  — значением.
 
Но может случиться так, что двумерный массив будет состоять из двух подмассивов: подмассива ключей и подмассива значений:
Строка 1387 ⟶ 1337 :
 
==== Нет данных ====
 
Если нет данных, то лучше записать хеш как пару фигурных скобок:
 
Строка 1396 ⟶ 1345 :
xew #-> {5=>3, 1=>4, 2=>2}</code>
 
И уже по ходу дела разобраться, что к чему.
 
==== Известен только тип значений ====
Сведения о типе значений использовать следует так: создать хеш, в котором будет определен элемент по умолчанию. Элементом по умолчанию должен быть нулевой элемент соответствующего типа, то есть для строки это будет пустая строка (<code>""</code>), для массива — пустой массив (<code>[]</code>), а для числа — нуль (<code>0</code> или <code>0.0</code>). Это делается, чтобы к пустому элементу можно было что-то добавить и при этом не получить ошибку.
 
Сведения о типе значений использовать следует так: создать хеш, в котором будет определен элемент по умолчанию. Элементом по умолчанию должен быть нулевой элемент соответствующего типа, то есть для строки это будет пустая строка ( <code>""</code> ), для массива — пустой массив ( <code>[]</code> ), а для числа — нуль (<code>0</code> или <code>0.0</code>). Это делается, чтобы к пустому элементу можно было что-то добавить и при этом не получить ошибку.
<source lang=ruby>xew = Hash.new( "" )
xew[ "песенка про зайцев" ] += "В темносинем лесу"
Строка 1420 ⟶ 1368 :
 
=== Методы работы с ассоциативными массивами ===
 
Когда речь пойдет о методах, которые присутствуют в ассоциативных массивах, то частенько будет возникать чувство [[w:Дежавю|дежавю]]. Во всяком случае, учить заново итераторы вам не придется. Вполне естественно, что появятся новички, но их будет немного. Тем не менее, прилежный преподаватель первым делом представляет новичков группе. Поэтому и мы начнем с тех методов, которые будут необходимы нам при работе с ассоциативными массивами, но отсутствуют у индексных.
 
Строка 1432 ⟶ 1379 :
 
==== Замена ключей на значения ====
 
Чтобы поменять местами ключи и значения ассоциативного массива, следует применять метод <code>.invert</code>. Этот метод возвращает асоциативный массив с ключами, замененными значениями, и значениями, замененными ключами.
 
Строка 1446 ⟶ 1392 :
 
==== Обновление пары ====
 
Что вы делаете, если хотите обновить какую-то программу или игру? Правильно, устанавливаете [[w:Компьютерный сленг|апдейт]]. А вот чтобы обновить значение в ассоциативном массиве используется метод <code>.update</code>. Пример использования этого метода в боевых условиях мы уже приводили в начале раздела. Если вы помните, то мы считали ''сколько раз повторяется каждое число''. Наверняка, вы немного подзабыли его решение (у программистов есть привычка не помнить [[w:Константа|константы]]). Позволю себе его вам напомнить:
 
Строка 1457 ⟶ 1402 :
<code>result.update<u>( { i=>1 } )</u>{ |key,old,new| old+new }</code>
 
Сразу после названия метода (в нашем случае <code>.update</code>) идет передача параметра. Страшная запись <code>{ i => 1 }</code>   это не что иное, как еще один хеш. Ключ его хранится в переменной <code>i</code> (счетчик итератора <code>.inject</code>), а в качестве значения выбрана единица. Зачем? Расскажу чуть позже.
 
{{info|Не обязательно писать именно <code><nowiki>{ i=>1 }</nowiki></code>. Можно "сократить" фигурные скобки и записать <code><nowiki>i=>1</nowiki></code>}}
 
{{Рамка}}'''Счетчик итератора'''  — это переменная в которую итератор записывает текущий элемент последовательности.{{Акмар}}
 
Здесь вроде бы все понятно. Запись стала менее страшной, но все равно вызывает дрожь. Будем это исправлять!
Строка 1471 ⟶ 1416 :
<code>result.update( { i=>1 } ){ |key,old,new| <u>old+new</u> }</code>
 
Всего лишь сложение <code>old</code> и <code>new</code>. Ничего не говорит? Тогда расскажу, что значат переменные <code>key</code>, <code>old</code> и <code>new</code>. В переменную <code>key</code> передается значение текущего ключа, в <code>old</code>  — старое значение ключа (''old'' по английски значит "«старый"»), а в переменную <code>new</code>  — добавляемое значение ключа (''new'' по английски значит "«новый"»).
 
Теперь переведем запись <code>old+new</code> на русский: в случае обнаружения ключа в хеше, нам необходимо сложить старое значение с новым. Если помните, то новое значение равняется единице, то есть в случае когда ключ, хранимый в <code>i</code> уже есть в хеше <code>result</code>, то к старому значению просто добавляется единица. Вот и все...все… а вы боялись.
 
{{info|Рекомендуется перечитать данную главу еще раз, так как вы ее немного не поняли}}
Строка 1480 ⟶ 1425 :
 
==== Размер ассоциативного массива ====
 
Ну вот, с новичками мы познакомились, теперь можно переходить к старым знакомым. Помните, как мы находили размер массива? Вот и с хешами точно также:
 
Строка 1489 ⟶ 1433 :
 
==== Удаление пары по ключу ====
О том, как добавлять элементы в массив мы знаем, а вот про удаление — нет! Необходимо это исправить. Чем мы сейчас и займемся.
 
О том, как добавлять элементы в массив мы знаем, а вот про удаление — нет! Необходимо это исправить. Чем мы сейчас и займемся.
 
<code>xew = {<u>5=>1</u>, 1=>3, 2=>4, 3=>1, 4=>1}
Строка 1497 ⟶ 1440 :
xew.delete( 5 ) #-> nil</code>
 
Как вы, наверно, уже догадались, удалением пары по ключу занимается метод <code>.delete</code>. Ему передается ключ от пары, которую следует удалить.
 
{{info|
Строка 1505 ⟶ 1448 :
 
==== Удаление произвольной пары ====
 
Многие программисты удивляются, когда узнают, что ассоциативные массивы имеют метод <code>.shift</code>. Связано это удивление с тем, что у индексных массивов он ''удаляет первый элемент, возвращая его во время удаления''. А вот как понять, какая пара является первой? И что такое ''первый в неупорядоченной последовательности пар''?
 
Строка 1523 ⟶ 1465 :
 
==== Преобразовать в индексный массив ====
 
Чуть ранее уже говорилось, что в большинстве случаев индексные массивы удобней ассоциативных.
 
{{Рамка}}Некоторые программисты утверждают, что при больших объемах данных лучше использовать двумерный индексный массив. Получается примерно то же, что и хеш (лишь поиск элемента по ключу осуществить сложнее), но обычно программа работает быстрей.
 
Мнение авторов таково, что у программиста на Руби есть более благородные пути времяпровождения, чем заниматься такой вот псевдооптимизационной ерундой.{{Акмар}}
Строка 1545 ⟶ 1486 :
 
==== Упорядочение хеша ====
 
Да, множество пар в хеше неупорядоченно. Но это можно исправить, разве что результат потом будет не хешем, а двумерным массивом.
 
Строка 1561 ⟶ 1501 :
 
==== Поиск максимальной/минимальной пары ====
 
Максимальная пара в хеше ищется точно также, как и максимальный элемент в массиве
 
Строка 1569 ⟶ 1508 :
 
но с небольшими особенностями:
* результат поиска  — массив из двух элементов вида <code>[ключ, значение]</code>
* сначала поиск происходит по ключу, а в случае совпадения ключей  — по значению
 
Несколько больше возможностей приобрели методы .max_by и min_by
Строка 1583 ⟶ 1522 :
 
=== Логические методы ===
Работа логических методов похожа на допрос с пристрастием. Помните, как в детективах во время теста на детекторе лжи, главный герой восклицал: «Отвечать только да или нет!» Если перевести это на язык Ruby, то это будет звучать примерно так: «Отвечать только true или false!»
 
В детективах набор вопросов стандартен:
Работа логических методов похожа на допрос с пристрастием. Помните, как в детективах во время теста на детекторе лжи, главный герой восклицал: "Отвечать только да или нет!" Если перевести это на язык Ruby, то это будет звучать примерно так: "Отвечать только true или false!"
* знали ли вы мистера Х?
 
* вы были на месте преступления?
В детективах набор вопросов стандартен:
* знали ли вы мистера Х?
* вы были на месте преступления?
* убивали ли мистера Х
* ...
На Ruby примерно тоже самое:
* ты пустой?
* есть ли такой элемент?
Строка 1600 ⟶ 1538 :
 
==== Хеш пустой? ====
Зададим вопрос «Хеш пустой?», но используя известный нам [[w:Лексикон|лексикон]]. Для начала спросим «Пустой хеш тебе не [[w:Брат|брат]]-[[w:Близнецы|близнец]]?»
 
Зададим вопрос "Хеш пустой?", но используя известный нам [[w:Лексикон|лексикон]]. Для начала спросим "Пустой хеш тебе не [[w:Брат|брат]]-[[w:Близнецы|близнец]]?"
<source lang=ruby>nycTou_xew = {}
noJIHbIu_xew = { "гаечный" => 20, "замочный" => "английский", "разводной" => 34 }
Строка 1608 ⟶ 1545 :
noJIHbIu_xew == {} #-> false</source>
 
Можно спросить по другому  "«Размер у тебя не нулевой?"»
<source lang=ruby>nycTou_xew = {}
noJIHbIu_xew = { "гаечный" => 20, "замочный" => "английский", "разводной" => 34 }
Строка 1622 ⟶ 1559 :
noJIHbIu_xew.empty? #-> false</source>
 
а то еще примут нас за приезжих...приезжих…
 
{{info|Обратите внимание, что метод <code>.empty?</code> полностью повторяет такой же метод у индексных массивов}}
 
==== Есть такой ключ? ====
Если вам нужно узнать у хеша ответ на вопрос «Есть у тебя такой ключ?», но вы не знаете как это правильно спросить, то скорее всего вы зададите вопрос в два этапа: «какие ключи у тебя есть?» и «есть среди них такой ключ?»
 
Если вам нужно узнать у хеша ответ на вопрос "Есть у тебя такой ключ?", но вы не знаете как это правильно спросить, то скорее всего вы зададите вопрос в два этапа: "какие ключи у тебя есть?" и "есть среди них такой ключ?"
 
<code>kapmaH = { "гаечный" => 20, "замочный" => "английский", "разводной" => 34 }
kapmaH<u>.keys.include?( "гаечный" )</u> #-> true</code>
 
В данном примере у нас в <code>kapmaH</code>`e нашелся "«гаечный"» ключ.
 
Но лучше задавать вопрос напрямую, это покажет ваше прекрасное знание языка.
Строка 1650 ⟶ 1586 :
 
==== Есть такое значение? ====
Давайте подумаем, как задать вопрос «Есть такое значение?» хешу. Скорее всего, мы опять зададим вопрос в два этапа: «какие значения есть?» и «есть ли среди них нужное нам?»
 
Давайте подумаем, как задать вопрос "Есть такое значение?" хешу. Скорее всего, мы опять зададим вопрос в два этапа: "какие значения есть?" и "есть ли среди них нужное нам?"
 
<code>kapmaH = { "гаечный" => 20, "замочный" => "английский", "разводной" => 34 }
Строка 1665 ⟶ 1600 :
 
=== Итераторы ===
 
У ассоциативных массивов есть следующие итераторы:
* <code>.find_all</code>  — поиск всех элементов, которые удовлетворяют логическому условию
* <code>.map</code>  — изменение всех элементов по некоторому алгоритму
* <code>.inject</code>  — сложение, перемножение и агрегация элементов массива
 
Набор итераторов точно такой же, как и у индексных массивов  — сказывается их родство. Вот только ведут себя они несколько иначе:
* результатом является двумерный массив (как после метода <code>.to_a</code>)
* в качестве счетчика (переменной в фотографии) передается массив вида <code>[ключ, значение]</code>
Строка 1707 ⟶ 1641 :
#-> Ошибка в методе '+' невозможно сложить nil и число типа Fixnum</code>
 
Обратите внимание, что развертка массива прошла успешно только в первых двух итераторах. В третьем возникла ошибка. Давайте выясним, откуда там взялся <code>nil</code>. Дело в том, что развернуть массив не удалось и теперь он стал называться не <code>array</code>, а <code>key</code>. Переменная <code>value</code> осталась "«не у дел"» и ей присвоилось значение <code>nil</code>. Чтобы это исправить, достаточно поставить круглые скобки:
 
<code>xew.inject(0){ |result, <u>(key, value)</u>| result + <u>value</u> }
Строка 1714 ⟶ 1648 :
<!-- В рамках данного учебника, итераторы, не возвращающие измененного объекта, рассматриваться не будут.
Это связано с тем, что задачи решенные при помощи методов: .each, .times, each_index; больше похожи на адаптацию
к решениям на языка типа Си, Паскаль и  т.  д. Мы пойдем другим путем! =)
Не стоит вызывать внутри блока метод вывода на экран. Это тоже "«попахивает"» ошибкой или отладкой. -->
 
<!-- по поводу puts согласен, а each и each_pair - — важные штуки, которые как-то жалко упускать. each это же столп Enumerable :-) -->
<!-- Вот когда будем примеси рассказывать, то там и будем про .each втирать. А сейчас только самые важные методы, которые позволят
решать задачи в функциональном стиле.-->
<!-- Верно, мы ведь повествуем по спирали. -->
<!-- Я тему раскрыл? Этот кусок можно удалять?
Ассоциативный массив, как и индексный массив, имеет метод <code>.map</code>, который передает блоку ключ и соответствующее ему значение. При этом в блок на самом деле передается массив с ключом и значением, но Ruby "«разворачивает"» их в две переменные при передаче блоку.
 
Итератор <code>.map</code>, в свою очередь, возвращает индексный массив с результатами блока  — по элементу массива на каждый ключ:
 
<code>xew = {"гаечный ключ" => 4, "разводной ключ" => 10}
Строка 1736 ⟶ 1670 :
 
=== Хитрости ===
 
Одному программисту надоело писать <code>xew["key"]</code> и он захотел сделать так, чтобы можно было написать <code>xew.key</code>.
 
Строка 1755 ⟶ 1688 :
 
== Подробнее о строках ==
 
{{info|Строка — это упорядоченная последовательность символов, которая располагается между ограничительными символами}}
 
Строковый тип является самым популярным в любом языке программирования. Ведь без него невозможно написать любую программу (особенно учитывая, что любая программа  — это строка). При выводе на экран или записи в файл, любой тип данных преобразовывается к строке (явно или неявно). Это значит, что в конечном итоге все сводится к строковому типу. Кстати, и ввод данных тоже осуществляется в виде строки (и только потом преобразовывается в другие типы).
 
{{Начало цитаты}}Студенты 4-го курса МЭТТ ГАИ поступили на подготовительные курсы в МГИУ. Там им начали преподавать основы программирования на Ruby. И одна из заданных им задач была: "«Дано число, необходимо поменять порядок цифр на обратный"». Задача сложная, но наши студенты об этом не знали и решили ее преобразованием к строке: <code>ucxogHoe.to_s.reverse</code>. Преподаватели были поражены и впредь запретили им использовать преобразования к строке в своих программах. И все потому, что это сильно упрощало решение и давало студентам огромное преимущество перед остальными слушателями курсов.{{Конец цитаты}}
 
Язык Ruby унаследовал работу со строками из языка Perl (признанного лидера по работе со строками). В частности такой мощный инструмент как «правила» (rules).
 
{{info|Раньше «правило» называлось «регулярным выражением», но в виду того, что на данный момент механизм «правил» вышел далеко за пределы регулярной грамматики, то решено его было переименовать (дабы не смущать умы образованной общественности)}}
 
Но наследование не подразумевает бездумного копирования. В частности, «правила», в рамках Ruby, получили объектно-ориентированную реализацию, что позволяет применять к ним различные методы. Помимо «правил», присутствует великое множество методов работы со строками. Причем некоторые из них являются нашими старыми знакомыми (<code>+</code>, <code>*</code>, <code>[]</code> и  т. д.). Работают они несколько иначе, но некоторая параллель с массивами все же присутствует.
Следует упомянуть два очень интересных момента:
* Cтроки — это универсальный тип данных, т.к.так как в строку можно преобразовать любой другой тип данных. А также, строку можно преобразовать в любой другой тип данных (ведь изначально любой код программы — это строка).
* Cтроки очень удобно преобразовывать в массив и обратно (методы <code>.join</code> и <code>.split</code>). Поэтому работа со строками практически такая же удобная, как и с массивами.
{{Внимание|Если работа со строками обходится без преобразования в массив, то программа либо очень простая, либо бесполезная}}
 
=== Способы создания строки ===
Строка создается при помощи ограничительных символов. Для этих целей чаще всего используются "« (лапка) и ' (типографский апостроф/минута/одиночная кавычка). Их смысл различен. Строка в минутах гарантирует, что в ней будет содержаться текст такой же, как в коде программы, без изменений. Строка в лапках будет проходить предварительное преобразование. Будут раскрыты конструкции «вставка»„вставка“ и «специальный„специальный символ»символ“.
 
{{Начало цитаты}}
Зажатые в лапках работать хотят,<br />
а строки в минутах лениво сопят.
{{Конец цитаты}}
 
Давайте будем называть строки в минутах «ленивыми»„ленивыми“, а строки в лапках  «работящими»„работящими“.
 
{{info|«Вставка» — это хитрая конструкция, которая вставляется между ограничительными символами (внутрь строки). Она состоит из комбинации решетки и двух ушек ( <code><u>#{</u> 'здесь был Вася' <u>}</u></code> ). Внутри нее можно писать не только <code>'Здесь был Вася'</code>, но и любой программный код. Результат программного кода будет преобразован к строке и вставлен вместо «вставки»}}
 
{{Начало цитаты}}
«Вставка»„Вставка“ жизнью заправляет:<br />
Код программный выполняет,<br />
Тихо результат считает,<br />
Вместо «вставки»„вставки“ подставляет.
{{Конец цитаты}}
 
{{Внимание|«Вставка» работает только в момент создания строки. После создания придется придумывать другие способы подстановки данных в строки}}
 
Специальный символ начинается с знака <code>\</code> ([[w:Обратная косая черта|обратная косая черта]]). Самые популярные из них: <code>\n</code> (переход на новую строку), <code>\t</code> ([[w:Табуляция|табуляция]]), <code>\\</code> (обратная косая черта) и \"» ([[w:Кавычки|двойная кавычка]]).
 
{{Внимание|Хотя специальный символ и пишется, как два знака, но на деле это всего один символ. Доказать это можно выполненением простенького кода: <code>"\n".size #-> 1</code>}}
 
==== Для чего нужны работящие и ленивые строки? ====
 
Скорее всего вы будете редко вспоминать про то, что существуют работящие и ленивые строки. Тем более, что это различие действительно только на момент создания строки. Рядовой программист пользуется либо работящими, либо ленивыми строками. Давайте посмотрим, как выглядит код программиста, который использует только ленивые строки:
 
Строка 1818 ⟶ 1749 :
puts "Повторенье -- мать ученья. Мой массив = #{mou_maccuB.join(<s>\"</s>,<s>\"</s>)}"</code>
 
Программа вызовет ошибку, т.к.так как внутри «вставки» было использовано экранирование кавычек. Правильный пример будет выглядеть так:
 
<code>mou_maccuB = [1,2,3,4]
Строка 1837 ⟶ 1768 :
 
=== Методы работы со строками ===
 
Методы строк умеют:
* преобразовывать входные данные в красивый вид
Строка 1843 ⟶ 1773 :
* дезертировать в массивы
 
Допустим, вы нашли максимальный элемент массива. И вам надо вывести результат на экран.
Вы можете поступить вот так:
<source lang=ruby>maccuB = [4, 4, 2, 5, 2, 7]
puts maccuB.max #-> 7</source>
 
Несмотря на правильность решения, вывод результата выглядит некрасиво.
Гораздо профессиональней будет написать вот такую программу:
 
Строка 1876 ⟶ 1806 :
 
==== Арифметика строк ====
 
Кто бы мог подумать, но строки можно складывать, умножать и если постараться, то и делить. Естественно, что это не арифметические операции чисел, а методы со своим особенным поведением. Рассмотрим сложение, которое в строках работает как сцепление (конкатенация):
 
Строка 1909 ⟶ 1838 :
end</source>
 
Этот код как раз и говорит о том, что деление и <code>.split</code>  — одно и тоже.
 
{{info|Деление стало нужно, когда метод <code>*</code> для массивов получил возможность работать, как <code>.join</code> (преобразовать массив в строку, расположив элементы через разделитель). В виду того, что <code>.join</code> и <code>.split</code> работают вместе точно также, как умножение и деление, то появилась идея заставить работать деление как <code>.split</code>}}
 
==== Преобразование в массив или путешествие туда и обратно ====
 
Случилось так, что итераторы в строках работают настолько неуклюже, что рассмотрение их в рамках учебника будет пропущено. Следовательно, для того, чтобы задействовать механизм итераторов, мы будем преобразовывать строки в массив. После того, как итераторы нам станут не нужны, то мы вернемся к строкам.
 
Строка 1942 ⟶ 1870 :
 
==== Длина строки ====
 
Длина строки ищется точно также, как и длина массива или хеша, то есть методом <code>.size</code>:
 
Строка 1952 ⟶ 1879 :
 
==== Получение подстрок ====
 
Получение подстрок работает точно также, как и получение подмассива. С тем лишь отличием, что нумерация идет не по элементам, а по символам. Это логично, особенно, если учесть, что для строки элементом является символ.
 
Строка 1981 ⟶ 1907 :
 
==== Строка-перевертыш ====
 
Иногда хочется перевернуть строку задом наперед. Причины могут быть разные. Например, вы ищите палиндром (число, которое можно перевернуть без ущерба для его значения). Занимается этим благородным делом метод <code>.reverse</code>. Давайте сформируем сообщение для Иных по другую сторону Сумрака:
 
Строка 1992 ⟶ 1917 :
 
==== Меняю шило на мыло! ====
Для того, чтобы заменить <code>"шило"</code> на <code>"мыло"</code> используется не газета «Из рук в руки», а методы <code>.sub</code> и <code>.gsub</code>:
 
Для того, чтобы заменить <code>"шило"</code> на <code>"мыло"</code> используется не газета "Из рук в руки", а методы <code>.sub</code> и <code>.gsub</code>:
 
<code>"<u>шило</u> в мешке не утаишь".sub("шило","мыло") #-> "<u>мыло</u> в мешке не утаишь"</code>
Строка 2001 ⟶ 1925 :
<code>"Ирине <u>18</u> лет".sub("18","26") #-> "Ирине <u>26</u> лет"</code>
 
Заметили, что мы используем только метод <code>.sub</code>? Давайте теперь рассмотрим работу метода <code>.gsub</code> и его отличие от <code>.sub</code>. На этот раз мы будем исправлять текст, авторы которого забыли про правило "«ЖИ, ШИ  — пиши через И"»
 
<code>cTpoka = "жыло-было шыбко шыпящее жывотное"
Строка 2012 ⟶ 1936 :
 
{{Начало цитаты}}
Одну ошибку лишь исправил<br />
Ленивый метод <code>.sub</code><br />
Все остальные устранил<br />
Трудолюбивый <code>.gsub</code>
{{Конец цитаты}}
Строка 2023 ⟶ 1947 :
 
==== Сканируем текст на оШЫбки ====
Давайте, найдем и посчитаем о'''ШЫ'''бки. Искать мы будем методом <code>.scan</code>
 
Давайте, найдем и посчитаем о<b>ШЫ</b>бки. Искать мы будем методом <code>.scan</code>
 
<code>cTpoka = "<u>жы</u>ло-было <u>шы</u>бко <u>шы</u>пящее <u>жы</u>вотное"
Строка 2037 ⟶ 1960 :
 
{{Начало цитаты}}
Нашел о<b>'''ШЫ</b>'''бку метод <code>.scan</code>,<br />
В массив ее запомнил.<br />
Учителям он свыше дан!<br />
Зачем его я вспомнил?!
{{Конец цитаты}}
 
==== Правила работы со строками ====
Правила — это образцы к которым можно примерять строки. Правила обладают своим собственным языком, который позволяет описывать одну, две, сотню и вообще любое количество строк. Это своеобразная упаковка для множества строк в одну компактную запись.
 
Правила — это образцы к которым можно примерять строки. Правила обладают своим собственным языком, который позволяет описывать одну, две, сотню и вообще любое количество строк. Это своеобразная упаковка для множества строк в одну компактную запись.
 
Правила в Ruby ограничиваются символами <code>/</code> (наклонная черта). Примеры правил:
Строка 2064 ⟶ 1986 :
 
===== Символьный класс =====
Символьный класс — просто конечный набор символов. Он ограничивается квадратными скобками и содержит перечисление символов, которые можно вместо него подставить. Заменяется он всего на один символ, входящий в этот класс. Примеры символьных классов:
 
Символьный класс — просто конечный набор символов. Он ограничивается квадратными скобками и содержит перечисление символов, которые можно вместо него подставить. Заменяется он всего на один символ, входящий в этот класс. Примеры символьных классов:
 
<code>/<u>[абвгде]</u>/ #-> простое перечисление символов
Строка 2080 ⟶ 2001 :
{| style="margin:0.5em 0 0.5em 1em;" class="standard" border=1
|-
! Короткая<br />запись || Полная<br />запись || Описание
|-
| <code>\s</code> || <code>[\f\t\n\r ]</code> || пробельный символ
|-
| <code>\S</code> || <code>[^\f\t\n\r ]</code> || любой символ, кроме пробельного
Строка 2100 ⟶ 2021 :
 
===== Квантификатор =====
 
Квантификатор показывает сколько раз может повторяться предыдущий символ (группа, альтернатива или еще какая нечисть). Квантификатор ограничивается парой ушек (фигурными скобками). Примеры квантификаторов:
 
Строка 2116 ⟶ 2036 :
{| style="margin:0.5em 0 0.5em 1em;" class="standard" border=1
|-
! Короткая<br />запись || Полная<br />запись || Описание
|-
| <code>*</code> || <code>{0,}</code> || любое количество
Строка 2128 ⟶ 2048 :
 
===== Альтернатива =====
 
Альтернатива нужна, когда необходимо объединить несколько правил в одно. При этом совпадение засчитывается, когда есть совпадение хотя бы с одним правилом. Желательно альтернативу заключать внутрь группировки (круглые скобки). Правила, входящие в альтернативу, разделяются | (палкой, которая и является альтернативой). Примеры альтернатив:
 
Строка 2139 ⟶ 2058 :
 
===== Группировка =====
Группировка используется, когда необходимо обрабатывать результат частями. Например, при обработке ссылок в [[w:HTML|HTML]]-документе удобно отдельно обрабатывать текст ссылки и [[w:URL|URL]]. Группировка также как и альтернатива, заключается в круглые скобки. Более того, альтернатива обрабатывается как группировка. Доступ к результату совпадения каждой группировки осуществляется посредством специальных переменных <code>$1, $2, ..., $9</code>. Подробнее группировки будут рассмотрены в подразделе «Правильная замена». Пример использования группировки:
 
Группировка используется, когда необходимо обрабатывать результат частями. Например, при обработке ссылок в [[w:HTML|HTML]]-документе удобно отдельно обрабатывать текст ссылки и [[w:URL|URL]]. Группировка также как и альтернатива, заключается в круглые скобки. Более того, альтернатива обрабатывается как группировка. Доступ к результату совпадения каждой группировки осуществляется посредством специальных переменных <code>$1, $2, ..., $9</code>. Подробнее группировки будут рассмотрены в подразделе "Правильная замена". Пример использования группировки:
 
<source lang=ruby>"2+7*3".gsub(/(\d+)\*(\d+)/){ $1.to_i * $2.to_i } #-> "2+21"</source>
Строка 2149 ⟶ 2067 :
 
===== Фиксирующая директива =====
Фиксирующие директивы — это символы, которые привязывают правило к некоторому признаку. Например, к концу или началу строки.
 
Фиксирующие директивы — это символы, которые привязывают правило к некоторому признаку. Например, к концу или началу строки.
 
<code>/<u>^</u>\d+/ #-> строка начинается с числа
Строка 2156 ⟶ 2073 :
/<u>^$</u>/ #-> пустая строка</code>
 
Насколько видно из примеров,
* <code><u>^</u></code>  — привязка к началу строки
* <code><u>$</u></code>  — привязка к концу строки
 
{{Внимание|Фиксирующих директив гораздо больше двух. Об остальных читайте в специализированной литературе}}
 
===== Модификатор =====
 
Модификатор предназначен для изменения поведения правила. Он размещается сразу же после правила (после последней наклонной черты). Пример использования модификатора:
 
Строка 2170 ⟶ 2086 :
Бывают следующие модификаторы:
* <b>'''m</b>'''ultiline  — перенос строки считается простым символом
* <b>'''i</b>'''gnorcase  — поиск без учета регистра
* e<b>'''x</b>'''tended  — игнорировать пробельные символы
 
{{Внимание|Игнорирование регистра работает только для латиницы}}
Строка 2183 ⟶ 2099 :
 
==== Правильное разбиение ====
 
Разбиение называется «правильным» тогда, когда в качестве аргумента метода <code>.split</code> используется правило. Например, можно разбить текст по знакам препинания. Для этого необходимо выполнить следующий код.
 
Строка 2191 ⟶ 2106 :
 
==== Правильная замена ====
 
С правильной заменой не все так просто. Дело в том, что методы <code>.sub</code> и <code>.gsub</code> совместно с правилами становятся итераторами, которые последовательно обрабатывают каждое совпадение с правилом. Чтобы это увидеть в действии, давайте решим задачу исправления ошибок:
 
Строка 2197 ⟶ 2111 :
#-> "<u>Жы</u>ло-было <u>ши</u>бко <u>ши</u>пящее <u>жи</u>вотное"</code>
 
Опаньки, а первое слово не исправилось! Видимо дело в том, что слово "«Жыло"» начинается с прописной буквы. Сейчас исправим:
 
<code>"<u>Жы</u>ло-было шыбко шыпящее жывотное".gsub(/(<u>Ж|Ш|</u>ж|ш)ы/){ $1 + 'и' }
Строка 2206 ⟶ 2120 :
<code>/(Ж|Ш|ж|ш)ы/</code>
 
Оно состоит из двух частей:
* альтернативы с группировкой  — (Ж|Ш|ж|ш)
* символа  — ы
 
В альтернативе мы указали буквы с которых начинается неправильный слог. Символ просто добавляется к букве из альтернативы.
 
Зачем была использована группировка? Для пояснения причины, рассмотрим код в ушках:
Строка 2230 ⟶ 2144 :
 
Есть еще пару интересных моментов, которые вам необходимо знать. Во время предыдущего примера вас могли посетить следующие вопросы:
* А как получить весь текст, который совпал с правилом?
* Неужели необходимо делать всеобщую группировку?
 
Ответ на эти вопросы одназначный  — нет! Достаточно придумать название переменной (которая будет содержать совпавший текст) и правильно описать внутри ушек:
 
<code>"Раз, два, три!".gsub(/[а-я]+/){ <u>|cJIoBo|</u> cJIoBo.reverse }
Строка 2241 ⟶ 2155 :
 
==== Правильный поиск ====
 
Вот здесь метод <code>.scan</code> может развернуться в полную силу. Хотите получить массив всех русских слов в тексте? Запросто:
 
Строка 2261 ⟶ 2174 :
Выполните ее, посмотрите результат, а потом замените любую из группировок (?:…) на (…) и снова взгляните на рузультат.
 
Ну со <code>.scan</code> должно быть все понятно. А вот то, что метод <code>[]</code> начинает тоже правильно искать  — пока нет.
 
<code>"Раз, два, три!"<u>[/[А-Яа-я]+/]</u> #-> <u>"Раз"</u></code>
Строка 2267 ⟶ 2180 :
{{Внимание|Если методу <code>[]</code> передать в качестве параметра правило, то он вернет либо совпадение с правилом, либо <code>nil</code>}}
 
Очень полезно использовать <code>[]</code> в ситуациях, когда надо узнать ответ на вопрос "«Есть хотя бы одна подстрока, которая удовлетворяет правилу?"» или получить первое (или единственное) совпадение с правилом.
 
{{info|Существует древнее поверье, что если использовать одно и тоже правило для <code>.scan</code> и <code>.split</code>, то получаются две части текста, из которых реально получить исходный.
Строка 2276 ⟶ 2189 :
 
==== Жадность ====
 
Речь пойдет о жадности среди квантификаторов. Возьмем некоторый квантификатор <code>{n,m}</code> и посмотрим как он работает.
 
Строка 2295 ⟶ 2207 :
 
=== Хитрости ===
 
==== Перенос по словам ====
Несколько лет назад (еще при жизни http://ruby-forum.ru) решали мы интересную задачу: как реализовать автоматический перенос на новую строку (wrap). Для тех, кто не застал те времена, уточню задание: дан текст, необходимо, вставить переносы таким образом, чтобы каждая из полученных строк была меньше n (для определенности <code>n=80</code>). Недавно я осознал, что не могу решить эту задачу тем способом, который был нами тогда найден. Я его просто не помнил...помнил… Решение нашлось быстро, достаточно было вспомнить, что на англ. языке эта задача именуется коротким и емким словом wrap.
 
<source lang=ruby>class String
Строка 2304 ⟶ 2217 :
end</source>
Немного о структуре кода...кода… метод <code>.wrap</code> реализован для экземпляров класса <code>String</code>. Также стоит обратить внимание на то, что внутри правила (регулярного выражения) возможна "«вставка"» (как в "«рабочих строках"»). Используется сей метод следующим образом:
 
<source lang=ruby>p "wrapping text with regular expressions".wrap( 10 )
Строка 2314 ⟶ 2227 :
Очевидно, что оно состоит из четырех частей:
* '''(.{1,80})'''  — строка длиной от 1 до 80 символов (любых). Результат группировки записывается в <code>$1</code> (один доллар) или <code>"\\1"</code>
* '''( +|$\n?)'''  — пробелы или конец строки. Результат группировки записывается в <code>$2</code> (два доллара) или <code>"\\2"</code>. Обратите внимание на запись <code>$\n?</code>, которая означает "«конец строки (<code>$</code>), после которого может идти перенос (<code>\n</code>)"». Обратите внимание, что <code>$2</code> мы не используем и поэтому можно использовать <code>(?: )</code> (группировку без сохранения результата)
* '''|'''  — или
* '''(.{1,80})'''  — строка длиной от 1 до 80 символов (любых). Результат группировки записывается в <code>$3</code> (три доллара) или <code>"\\3"</code>
 
В результате работы этого правила произойдет сопоставление с группировками 1 и 2 или с группировкой 3. В первом случае, будет обрабатываться строка, слова в которой по длине не превышают 80. Во втором случае, строка будет принудительно усечена до 80 символов. Другими словами, мы пытаемся сделать перенос по словам, но если у нас не получается, то мы будем делать перенос так, как у нас получится.
Строка 2338 ⟶ 2251 :
p "Это #{cont}" #-> "Это контейнер"</source>
 
Если нужно, чтобы ваши обьекты упорядочивались и сравнивались с обычными строками, следует применять примесь <code>Comparable</code> и единственный специальный метод <code>to_str</code>. Наличие этого метода у вашего объекта  — знак для Ruby, что для сравнения следует применять не встроенный в <code>String</code> метод, а ваш.
 
<source lang=ruby>class Container
Строка 2363 ⟶ 2276 :
# Дана строка, содержащая кириллицу, латиницу и цифры. Вывести все слова, длина которых равна средней.
# Найти в строке первое целиком кириллическое слово.
# Дан текст (строка с переносами). Найти все слова, содержащие лишь три буквы "«о"».
#* Только для русских слов.
#* Для французских и русских слов.
#* Для любого регистра буквы "«о"».
# Найти в тексте время в формате "«часы: минуты: секунды"».
# Найти все слова без повторяющихся букв (например, "«Лисп"» или "«Ruby"», но не "«Паскаль"» или "«Java"»).
#* Только для русскоязычных слов.
#* Не учитывайте цифры в словах.
# Найти в тексте слова, содержащие две прописные буквы и исправить.
#* Решите задачу для слов и в кириллице, и в латинице.
# Найти в тексте даты формата "«день.месяц.год"».
#* Найдите дату, где день ограничен числом 31, а месяц 12. Год ограничивайте четырёхзначными числами.
#* Распознавайте месяц в виде "«31.марта.2001"».
# Дан текст. Найдите все URL адреса и вычлените из них только корневой домен (например, из http://ru.wikibooks.org/wiki/Ruby сделайте http://ru.wikibooks.org ).
 
== Подробнее о методах ==
 
Все функции в Ruby являются методами, то есть свойственны обьектам. При программировании на это можно не обращать внимания, поскольку любая программа на Руби уже является определением класса. У методов могут быть обязательные или необязательные параметры. Методы разграничиваются фигурными скобками или ключевыми словами <code>def</code> и <code>end</code>.
 
=== Создание метода ===
Благодаря тому, что указание класса-носителя метода необязательно, на Ruby можно программировать в функциональном стиле, не заботясь о создании класса-"«носителя"» для каждой группы методов. Метод создается с помощью ключевых слов <code>def ... end</code>
 
<source lang=ruby>def sum(a, b)
Строка 2390 ⟶ 2302 :
sum(10, 2) #-> 12</source>
 
Ruby по умолчанию возвратит из метода результат последнего выполненного выражения, поэтому в конце метода или в условных конструкциях слово return можно опускать. Поскольку методы могут быть переопределены в процессе выполнения программы, можно "«на ходу"» переписать метод так:
 
<source lang=ruby>def sum(a, b)
Строка 2417 ⟶ 2329 :
 
==== Указание значений по умолчанию ====
У методов могут быть необязательные аргументы; для этого им нужно присвоить значение, которое следует применять «по умолчанию»
 
У методов могут быть необязательные аргументы; для этого им нужно присвоить значение, которое следует применять "по умолчанию"
 
<source lang=ruby>def sum(a, b = 5)
Строка 2427 ⟶ 2338 :
 
==== Методы с восклицательным и вопросительным знаком ====
В Ruby при создании методов можно применять простейшую пунктуацию. Два стандартных приема применения такой пунктуации — восклицательный и вопросительный знак в конце метода. Методы с вопросительным знаком традионно работают как ''предикаты'', то есть возвращают <code>true</code> или <code>false</code>. Пример методов-предикатов, — методы массива.
 
В Ruby при создании методов можно применять простейшую пунктуацию. Два стандартных приема применения такой пунктуации - восклицательный и вопросительный знак в конце метода. Методы с вопросительным знаком традионно работают как ''предикаты'', то есть возвращают <code>true</code> или <code>false</code>. Пример методов-предикатов, — методы массива.
Например, в [[w:Java|Java]] подобные методы начинались бы со слова <code>is</code>: <code>isVolatile()</code>, <code>isEnabled</code>
 
Строка 2451 ⟶ 2361 :
Если вы реализуете программу, которой будут пользоваться другие, считается хорошим тоном реализовывать методы-предикаты.
 
Еще одна их прелесть  — сочетание с модификаторами выражения:
 
<source lang=ruby>arr = [1,2 ,3]
Строка 2464 ⟶ 2374 :
 
==== Методы присвоения ====
Другие особые варианты пунктуации — знак равенства и арифметические знаки.
 
Другие особые варианты пунктуации - знак равенства и арифметические знаки.
 
Знак равенства в конце названия метода означает, что этот метод присваивает свойству объекта значение:
Строка 2483 ⟶ 2392 :
 
==== Операторы ====
Операторы (умножение, деление, возведение в степень и так далее — вплоть до сравнения!) — тоже методы. Например:
 
Операторы (умножение, деление, возведение в степень и так далее - вплоть до сравнения!) - тоже методы. Например:
 
<source lang=ruby>class BeHuK
Строка 2506 ⟶ 2414 :
d + 1 #-> Mon Jun 12 - на день позже</source>
 
==== "«Поглощение"» аргументов метода ====
Можно «свернуть» аргументы с помощью звездочки — тогда метод получит массив в качестве аргумента
 
Можно "свернуть" аргументы с помощью звездочки - тогда метод получит массив в качестве аргумента
 
<source lang=ruby>def sum(*members)
Строка 2525 ⟶ 2432 :
sum(10, 2, 12, 34) #-> 58</source>
 
Можно разделить аргументы на обязательные и необязательные, просто пометив последний аргумент "«звездочкой"». Если методу будут переданы только обязательные аргументы, в переменной "«со звездочкой"» в теле метода будет пустой массив.
 
Звездочкой полезно пользоваться и когда нужно передать методу аргументы, но не хочется указывать их по отдельности. Следуя тому же примеру:
Строка 2533 ⟶ 2440 :
 
=== Подробнее о блоках ===
<!-- Кто-то говорил, что это неверно. Блоки блоками, а closures  — это замыкания. -->
Понятие [[w:Блок (программирование)|блока]] <!--([[w:Замыкание (программирование)|замыканий]])--> довольно просто: это часть программы, при создании захватывающая переменные окружающей среды. По сути блок есть анонимный метод.
 
Ruby позволяет создавать анонимные методы и передавать их функциям - — такие анонимные методы называются **блоками**. Очень большое количество функций Ruby основано
на использовании блоков - — например, итераторы (такие как <code>each</code> и <code>map</code>). Блок - — это фактически "«функция в функции" -» — программист определяет
операцию, которую необходимо выполнить, но непосредственно ее выполнение осуществляет метод, которому блок передается.
 
==== Зачем они нужны ====
Блоки позволяют избавиться от очень большого количества операций, которые для каждого программиста являются привычными — а именно…
 
Блоки позволяют избавиться от очень большого количества операций, которые для каждого программиста являются привычными - а именно...
 
* Поддержание индекса в цикле
Строка 2550 ⟶ 2456 :
 
==== Как создать блок ====
 
Блок передается методу через конструкцию <code>do... end</code> или фигурные скобки. Общепринятым является использовать фигурные скобки, если вызов блока умещается на одну строку программы. Для демонстрации работы блока, мы будем использовать метод <code>.map</code>. Этот метод принимает блок и выполняет его строго заданное число раз.
 
Строка 2576 ⟶ 2481 :
 
==== Блоки принимают аргументы ====
Другое замечательное свойство блоков — они, как и функции, могут принимать аргументы. В таком случае метод, которому передан блок, сам «решает», что этот блок получит в качестве аргумента. Например, уже продемонстрированный метод <code>.map</code> еще и передает блоку аргумент, который можно захватить следующим образом:
 
Другое замечательное свойство блоков — они, как и функции, могут принимать аргументы. В таком случае метод, которому передан блок, сам "решает", что этот блок получит в качестве аргумента. Например, уже продемонстрированный метод <code>.map</code> еще и передает блоку аргумент, который можно захватить следующим образом:
 
<source lang=ruby>puts (1..3).map do | index |
Строка 2584 ⟶ 2488 :
 
В данном случае при каждом выполнении блока переменная <code>index</code> будет устанавливаться на положение итератора, начиная с 1!
Аргументы метода указываются после открывающей фигурной скобки или после слова <code>do</code> через запятую, и разграничиваются двумя вертикальми чертами. Чтобы не перепутать черту со строчной латинской L, принято "«отбивать"» аргументы блока от вертикальной черты пробелами.
 
<source lang=ruby>method { | argument | .. }</source>
 
==== Свои методы с блоками ====
 
Ключевое слово <code>yield</code> в методе открывает раздвижные двери, впускающие аргумент[ы] в блок.
 
Строка 2639 ⟶ 2542 :
#-> ??? u gBa</source>
 
Здесь lambda - — пустая функция, а block.call - — явный способ вызова блока кода на выполнение.
 
Блок можно также передать другому методу, просто указав его как последний аргумент с амперсандом:
Строка 2648 ⟶ 2551 :
 
==== Некоторые применения блоков ====
Блоки — одна из главных особенностей Ruby. Уметь ими пользоваться — ключ к очень коротким и очень понятным программам, делающим очень много.
 
Типичное применение блока — когда после выполнений некой операции нужно «вынести мусор»: закрыть открытый ресурс или отсоединиться от сети. Предположим, что мы пишем метод для интернет-системы. При этом мы хотим выполнить несколько операций, но чтобы их выполнить, нужно подключить пользователя к Сети. После того, как операции завершились, надо его так же незаметно отключить.
Блоки — одна из главных особенностей Ruby. Уметь ими пользоваться — ключ к очень коротким и очень понятным программам, делающим очень много.
 
Типичное применение блока — когда после выполнений некой операции нужно "вынести мусор": закрыть открытый ресурс или отсоединиться от сети. Предположим, что мы пишем метод для интернет-системы. При этом мы хотим выполнить несколько операций, но чтобы их выполнить, нужно подключить пользователя к Сети. После того, как операции завершились, надо его так же незаметно отключить.
 
<source lang=ruby>connected { download_email }</source>
 
В данном случае мы пишем только блок с <code>download_email</code>, все заботы по открытию (а главное  — закрытию) соединения возьмет на себя метод <code>connected</code>:
 
<source lang=ruby>def connected
Строка 2665 ⟶ 2567 :
 
В данном случае мы сохраняем то, что вернул блок в метод, закрываем соединение и возвращаем результат блока как свой собственный.
Чаще всего о методах, принимающих блоки, можно говорить как о деепричастном обороте  — например "«соединившись"», "«внутри_транзакции"», "«с файлом"», "«трижды"».
 
Если воспользоваться встроенной проверкой исключений, то метод принимает такой вид:
Строка 2682 ⟶ 2584 :
 
=== Методы, которых не было ===
Экспериментально замечено, что во время сессии у студентов в разы повышается способность к изобретениям различного рода. Иногда, удается направить эту энергию в мирное русло: некоторые студенты во время сдачи зачета начинают <u>придумывать свои методы<u>. Естественно, что «придуманные методы» они реализовать не могут, но с этим замечательно справляются их преподаватели. Некоторым методам даже дают имена студентов, которые приложили свое незнание к их созданию. Многие из таких методов включают в последующие версии языка.
 
Экспериментально замечено, что во время сессии у студентов в разы повышается способность к изобретениям различного рода. Иногда, удается направить эту энергию в мирное русло: некоторые студенты во время сдачи зачета начинают <u>придумывать свои методы<u>. Естественно, что "придуманные методы" они реализовать не могут, но с этим замечательно справляются их преподаватели. Некоторым методам даже дают имена студентов, которые приложили свое незнание к их созданию. Многие из таких методов включают в последующие версии языка.
 
==== Ширяевский .size ====
 
Студент МЭТТ Ширяев Денис, на одном из зачетов предложил использовать метод <code>.size</code> в качестве итератора. Он использовал его для подсчета количества элементов массива, удовлетворяющих условию. По сути, он предложил укоротить связку <code>.find_all{ ... }.size</code>. Вот как будет выглядеть программа подсчета количества четных элементов массива:
<source lang=ruby>maccuB = [1,2,3,4,5,6]
Строка 2701 ⟶ 2601 :
 
==== Случайное число из диапазона ====
 
Студенты часто возмущаются, почему чтобы получить случайное число от 3 до 6 нужно писать нечто невнятное вида:
<source lang=ruby>3 + rand( 4 )</source>
Строка 2737 ⟶ 2636 :
 
=== Способы расширения библиотеки методов ===
==== Как добавить метод к массиву/строке/венику? ====
 
==== Как добавить метод к массиву/строке/венику? ====
Важно помнить, что в Ruby все типы являются обьектами, даже сами классы. Каждый класс до конца выполнения программы остается открытым, а это значит, что в любой тип можно добавить собственные методы (или изменить поведение существующих). Каждый класс можно определять постепенно, в нескольких частях программы:
 
Строка 2757 ⟶ 2656 :
{{Внимание|метод <code>.instance_methods</code> возвращает массив, который содержит имена методов, которые можно вызвать.}}
 
Добавленные методы становятся доступны немедленно, в том числе для уже созданнных экземпляров типа. Стоит помнить, что методы в Ruby  — на самом деле "«сообщения"», и у каждого метода есть "«приемник"», то есть объект, которому сообщение отправлено. Метод по умолчанию ищет другие методы в экземпляре класса, поскольку приемником для него является <code>self</code>.
 
Простейший пример  — добавление метода классу <code>String</code>, выводящий только согласные буквы из строки:
 
<code>str = "Crazy fox jumps over a lazy dog"
Строка 2776 ⟶ 2675 :
{{Внимание|операция расширения класса (добавление нового метода к существующему) по сути не отличается от создания нового класса.}}
 
У обьектов в Ruby есть методы класса и методы экземпляра  — в нашем примере <code>consonants</code>  — это именно методы экземпляра. При создании нового класса или изменении существующего создать метод класса можно, начав его имя с имени класса или с <code>self</code> и точки:
 
<code>str = "Crazy fox jumps over a lazy dog"
Строка 2791 ⟶ 2690 :
String.consonants_from(str) #-> "Crfjmpsvlzdg"</code>
 
Одним из специфических свойств Ruby является то, что классы сами по себе  — экземпляры класса <code>Class</code>, и с ними можно работать как с обычными объектами. Специальный синтаксис для доступа к методам класса в Ruby не нужен. Классы можно хранить в переменных, передавать методам и так далее.
 
{{Внимание|В контексте класса <code>self</code> - это сам класс.}}
Строка 2816 ⟶ 2715 :
будут удалены. Так что даже в этом случае [[w:велосипед|велосипед]] изобретать не стоит!}}
 
Если к классу надо добавить много методов сразу, то при описании класса можно выйти на уровень его обьекта-класса. Это свойство в Ruby называется '''eigenclass''' ({{нем|eigen}} = "«свой, особый"»). Подозревая, что многие из читателей незнакомы с математическим понятием [[w:Собственные вектора, значения и пространства|собственного значения/вектора/пространства]], мы кратко и по-программистски назовём eigenclass '''эйгенклассом'''.
 
Добавим к классу <code>File</code> метод <code>myself</code>, который дает быстрый доступ к текущему файлу с исходным кодом:
Строка 2849 ⟶ 2748 :
other_string.vowels #-> NoMethodError: у <u>этой строки</u> нету метода `vowels'...</code>
 
Возможность добавлять и изменять устройство уже существующих классов  — одно из основных свойств Ruby, обеспечивающих великую гибкость языка. Часто бывает, что метод возвращает не тот результат, который нам нужен  — тогда при его изменении все программы, обращающиеся к данному методу будут получать измененный результат.
 
==== Программист-разрушитель ====
Как ни странно, изредка программисту приходится взять на себя позицию разрушителя — удалить существующий метод или константу. Метод <code>undef</code> позволяет сделать это:
 
Как ни странно, изредка программисту приходится взять на себя позицию разрушителя — удалить существующий метод или константу. Метод <code>undef</code> позволяет сделать это:
 
<code>class BeHuK
Строка 2915 ⟶ 2813 :
{{Начало цитаты}}При разработке своего Rails-приложения мной применялся класс <code>OrderedHash</code>, который работает как стандартный хеш, но при этом имеет упорядоченные ключи. Это позволяет, к примеру, удобно сгруппировать новости по датам, сохраняя порядок дат.''
 
''В какой-то момент моя программа перестала работать''. Почему? В Rails был, для внутренних нужд, добавлен другой класс <code>OrderedHash</code>, но при этом он не соответствовал моему (и даже не соответствовал обычному <code>Hash</code>  — некоторых методов в нем просто не было! Благодаря <code>remove_const</code> мне удалось просто выгрузить их класс и заменить его своим. А тесты в комплекте чужой библиотеки позволили удостовериться, что я ничего не испортил и она с моим "«внедренным"» классом функционирует нормально.{{Конец цитаты}}
{{Подпись|[[Участник:Julik|Julik]] 01:52, 25 июня 2006}}
 
=== Как написать свой итератор? ===
 
=== Как написать свой класс? ===
=== Наследовать или перемешать? ===
 
=== Как сделать свою библиотеку методов? ===
 
== Матрицы и вектора ==
Давно хотел написать историю про то, как использовать [[w:Матрица (математика)|матрицы]] и [[w:Вектор (математика)|вектора]] в Ruby, но руки дошли только сейчас. Мое знакомство с реализацией матричного исчисления в Ruby началось еще в мою бытность студентом [http://www.msiu.ru МГИУ]. И надо сказать, что это знакомство было неудачным. Дело в том, что простейшая программа вычисления определителя матрицы
<source lang=ruby>require 'matrix'
p Matrix[[1,-2,3],[3,4,-5],[2,4,1]].det #-> -50</source>
давала неверный результат (правильный ответ = 62). Как выяснилось позднее, эта проблема связана со спецификой целочисленной арифметики в Ruby (<одна вторая в Руби - — нуль>). Предположив это, я решил, что проблема легко решится, если я преобразую элементы матрицы к дробному типу (классу Float):
<source lang=ruby>require 'matrix'
p Matrix[[1.0,-2.0,3.0],[3.0,4.0,-5.0],[2.0,4.0,1.0]].det #-> 62.0</source>
И в некоторых случаях это меня действительно выручало. Но не всегда...всегда… стоило появиться делению на 3, как появлялись ненужные остатки и погрешности. И чем больше исходная матрица, тем больше вероятность появления таких остатков. В некоторых случаях это было несущественным, а в остальных приходилось прибегать к услугам специальных математических пакетов (например,[[w:Maxima|Maxima]]). Было жутко неудобно (я тогда писал курсовой, который решал все варианты контрольной работы по предмету <Математическое программирование>. Да простит меня преподаватель, который так и не понял секрета тотальной успеваемости группы) и обидно за такую "«кривую реализацию"».
 
На этом бы история и закончилась (как позже я узнал, на этом она заканчивалась для многих), но мне в руки попалась книга Programming Ruby 2ed с описанием возможностей стандартной библиотеки версии 1.8.2. Именно там (на стр. 671) я наткнулся на описание библиотеки mathn. Уникальность ее состоит в том, что она существенно расширяет возможности стандартных чисел, добавляя к ним [[w:Рациональное число|рациональные числа]] (класс <tt>Rational</tt>) и [[w:Комплексное число|комплексные числа]] (класс <tt>Complex</tt>).
{{info|Проще говоря, появляется возможность делить числа без погрешностей (класс <tt>Rational</tt>) и возможность извлекать квадратный корень из отрицательного числа (класс <tt>Complex</tt>)}}
Одновременно с этим она добавляет матричное и векторное исчисления (правда, почему-то в книге дополнительно подключали <tt>complex</tt> и <tt>matrix</tt>). И после этого матричное счисление начинает работать "«из коробки"» и еще как работать!!! Хотите обратую матрицу? Пожалуйста! Хотите определитель? Нет ничего проще! Обидно только одно...одно… программу к тому времени я уже написал и эти возможности мне были не нужны.
 
Чуть позднее, один из моих студентов написал мне письмо с просьбой объяснить как <работать с матрицами в Руби>? При этом он задал всего три вопроса:
Строка 2943 ⟶ 2843 :
 
=== Как создать новую матрицу? ===
Самый простейший способ создать матрицу - — использовать метод <батарейка> (метод [] выглядит как индикатор заряда батареи на сотовом телефоне):
<source lang=ruby>require 'mathn'
Matrix[[1, -2, 3], [3, 4, -5], [2, 4, 1]]</source>
Строка 2978 ⟶ 2878 :
p maTpuLLa #-> Matrix[[5, -2, 3], [3, 4, -5], [2, 4, 1]]</source>
 
Ну вот как-то примерно так...так… Почему не могли этого сделать разработчики, я так и не понял. Скорее всего по идеологическим соображениям (<не дело, чтобы матрицы вели себя как простые массивы>).
 
=== Методы работы с матрицами ===
Из методов работы с матрицами наиболее важные это: вычисление определителя (<tt>.det</tt>), вычисление обратной матрицы (<tt>.inv</tt>), поиск минора матрицы (<tt>.minor</tt>), преобразование матрицы в массив (<tt>.to_a</tt>), умножение (<tt>*</tt>), сложение (<tt>+</tt>), вычитание (<tt>-</tt>) и деление (<tt>/</tt>) матриц. Без остальных методов можно обойтись. Поэтому изучите их самостоятельно.
 
=== Зачем нужны вектора? ===
Во-первых, для Ruby вектор - — это объект класса <tt>Vector</tt>. Подключается он одновременно с матрицами (класс <tt>Matrix</tt>). Во-вторых, очень похож на массив, но с одним существенным отличием (cобственно это отличие и определяет полезность вектора): массивы и вектора по разному складываются и вычитаются. Давайте рассмотрим небольшой пример:
<source lang=ruby>require 'mathn'
maccuB = [1,2,3,4,5]
Строка 2998 ⟶ 2900 :
 
==== Решение систем линейных уравнений методом Гаусса ====
Как я уже рассказывал раньше, первая моя программа на Ruby (декабрь 2002 года) -- — реализация симплексного алгоритма для решения задач оптимизации. Весь фокус в том, что я эту программу нашел! Это 11 Кбайт и 363 строчки (140 + 223) программного кода. Кстати, если найду подходящее для нее место, то обязательно надо будет опубликовать.
 
Но суть не в этом. После того, как я ее нашел, у меня появилась идея-фикс: реализовать эту программу заново, но с высоты текущего уровня. Вот только для этого надо вспомнить хотя бы что-то по поводу симплексного алгоритма (чтение моего старого программного кода мне помогло мало). Первое, что пришло мне на ум -- — там был метод Гаусса (правда, насколько я помню, не в чистом виде). Вот как раз его мы сейчас и реализуем.
<source lang=ruby>require 'mathn'
ypaBHeHue = [Vector[1,2,1,1],Vector[1,5,6,2],Vector[1,5,7,10]]</source>
 
Почему был выбран именно массив векторов, а не матрица или двумерный массив? Дело в том, что в методе Гаусса приходится выполнять такие векторные операции, как: вычитание векторов и деление вектора на [[w:Скаляр|скаляр]]. Поэтому смысла создавать матрицу (векторных операций не предусмотрено) или двумерный массив (придется реализовывать эти операции) -- — нет! Кстати, вполне возможно создать массив векторов и из двумерного массива (что мы и сделаем в следующем примере). Итак, приступим к реализации прямого хода метода Гаусса:
<source lang=ruby>require 'mathn'
ypaBHeHue = [[1,2,1,1],[1,5,6,2],[1,5,7,10]].map{ |array| Vector[ *array ] }
Строка 3010 ⟶ 2912 :
ypaBHeHue[0] /= ypaBHeHue[0][0]</source>
 
И вот тут нас ждет неприятный сюрприз: у векторов не реализован метод /. Смотрим в файл <tt>matrix.rb</tt> (который мирно лежит в директории со стандартными библиотеками). Действительно нет! Метод * (умножить) есть, а разделить -- — нет. Ну и ладно, мы -- — программисты не гордые...гордые… сами напишем!
<source lang=ruby>require 'mathn'
 
Строка 3073 ⟶ 2975 :
p ypaBHeHue #-> [Vector[1,0,0,19], Vector[0,1,0,-13], Vector[0,0,1,8]]</source>
 
Ну вот и все...все… вроде как решение получили. Но было бы замечательно, если бы выводилось не все уравнение, а только столбец свободных членов. Задачка простенькая, но важная. Давайте ее решим:
<source lang=ruby>p ypaBHeHue.map{ |vector| vector.to_a }.transpose[-1] #-> [19,-13,8]</source>
 
Строка 3100 ⟶ 3002 :
p ypaBHeHue.map{ |vector| vector[-1] } #-> [19,-13,8]</source>
 
Обратите внимание, что <tt>ypaBHeHue</tt> задается через матрицу (которая, не без помощи метода <tt>.row_vectors</tt>, преобразуется в массив векторов). Также обратите внимание, что получить последний столбец можно посредством итератора <tt>.map</tt> и метода "«батарейка"».
 
Кстати, реализация симплексного алгоритма подразумевает еще несколько интересных задач:
# Если какой либо элемент главной диагонали нулевой, то необходимо переставить строки и/или столбцы, чтобы это исправить.
# Преобразование целевой функции и системы уравнений из текстового формата (<tt>2*x1-x2-x4 > min</tt> и <tt>x1-2*x2-2*x3-x4 <= 10</tt>) в <tt>YAML</tt>
# Первичная обработка целевой функции (если она стремится к min, то необходимо умножить вектор целевой функции на -1−1) и уравнений системы (если равенство не строгое, то преобразовать его в строгое, путем добавления новой неизвестной: <tt>x1-2*x2-2*x3-x4 &lt;= 10 > x1-2*x2-2*x3-x4+x5 = 10</tt>).
# Поиск ведущего элемента.
# Смена ведущего элемента.
Строка 3112 ⟶ 3014 :
 
== Работа с файлами ==
Файлы в программах играют роль хранилищ, в которые можно записать любые объекты. В отличие от привычных нам объектов, файлы позволяют хранить данные даже тогда, когда программа завершила свою работу. Именно поэтому они могут использоваться для передачи данных между разными программами или разными запусками одной и той же программы.
 
Файлы в программах играют роль хранилищ, в которые можно записать любые объекты. В отличие от привычных нам объектов, файлы позволяют хранить данные даже тогда, когда программа завершила свою работу. Именно поэтому они могут использоваться для передачи данных между разными программами или разными запусками одной и той же программы.
 
{{info|Среди профессиональных программистов принято сохранять настройки программы в файлы, чтобы их мог исправить пользователь или конфигурационная программа}}
Строка 3121 ⟶ 3022 :
Как организована работа с файлами? В самом общем случае работа с файлами состоит из следующих этапов:
# Открытие файла. Сущность этого этапа состоит в создании объекта класса <code>File</code>.
# Запись или чтение. Вызываются привычные нам методы вывода на экран и не совсем привычные  — ввода.
# Закрытие файла. Во время закрытия файла происходят действия с файловой системой. С объектом, который создается при открытии файла ничего не происходит, но после этого он указывает на закрытый файл и производить операции чтения/записи при помощи него уже нельзя.
 
Существует масса способов реализации работы с файлами:
# Чтение при помощи класса <code>IO</code>. Класс <code>IO</code> имеет множество методов, которые позволяют производить чтение из текстовых файлов (с "«двоичными файлами"» лучше так не работать). Если нужно считать весь текстовый файл, то лучше пользоваться методами класса <code>IO</code>.
# Перенаправление потоков. Существует три предопределенных переменных: <code>$stdio</code>, <code>$stdin</code> и <code>$stderr</code>. Если им присвоить объект класса <code>File</code> (создаваемый во время открытия файла), то весь вывод пойдет в файл, который присвоили переменной <code>$stdout</code>. Весь ввод будет браться из файла, который присволи переменной <code>$stdin</code>, а все ошибки будут сохраняться в файле, который присвоили переменной <code>$stderr</code>. Если нужно работать только с одним файлом на чтение и одним файлом на запись, то обычно используют этот способ. Также очень удобно использовать перенаправление потока ошибок (переменная <code>$stderr</code>) для программ, которые работают в составе пакетных файлов и используют только интерфейс командной строки.
# Универсальный способ. Используется в ситуациях, когда нельзя использовать предыдущие два способа.
Строка 3135 ⟶ 3036 :
 
=== Чтение при помощи класса IO ===
 
Для чтения файла целиком используется метод <code>.read</code>. Он считывает весь файл в виде строки. Во время его использования не стоит задумывать об открытии/закрытии файла, так как эти операции скрыты внутри метода.
 
Строка 3150 ⟶ 3050 :
 
=== Перенаправление потока ===
 
Очень часто программист проектирует программу таким образом, чтобы ввод данных осуществлялся с клавиатуры. После сотни-другой циклов откладки программист так устает вводить данные, что создает файл, который содержит все необходимую входную информацию и перенаправляет поток ввода с клавиатуры на этот самый файл. Добавляя всего одну строчку в начало своей программы:
<code>$stdin = File<u>.open('входные данные.txt')</u></code>
Строка 3156 ⟶ 3055 :
После этого, он сильно бьет себя по лбу и выкрикивает нечто вроде «Я идиот!» И все от осознания того, что процесс откладки давно можно было упростить таким способом.
 
А вот другая история. Программист пишет и отлаживает программу, которая все необходимые данных выводит на экран. Но в конечном итоге программа должна запускаться без участия человека и ее вывод нужно сохранять в файл (для дальнейшей обработки). Переписывать всю программу лень и поэтому в начало своей программы он вставляет парочку волшебных строчек:
<code>$stdout = File.open('выходные данные.txt',<u>'w'</u>)
$stderr = File.open('сообщения об ошибках.txt',<u>'a'</u>)</code>
Строка 3183 ⟶ 3082 :
<code>cTpoka = File.open('входные данные.txt'){ |file| file.read }</code>
 
Если необходимо записать данные, то нужно использовать модификатор доступа 'a' (добавление к концу файла) или 'w' (добавление в файл с его предварительной очисткой). Само собой разумеется, что запись данных в файл осуществляется методами puts, write и  т. д.
 
<code>File.open('выходные данные.txt','w'){ |file| file.write cTpoka }
File.open('выходные данные.txt','a'){ |file| file.puts cTpoka }</code>
 
Блок метода <tt>.open</tt> (т.е.то есть фигурные скобки) нужен для того, чтобы при выходе их блока автоматически осуществлять закрытие файла.
 
=== Хитрости ===
Строка 3195 ⟶ 3094 :
 
== Сети ==
 
=== Как написать [[w:Троянская программа|троян]]? ===
Однажды, один из студентов (<tt>Geka</tt>) попросил меня рассказать о том, как создать простейшее клиент-серверное приложение на Ruby. Перед тем, как подойти ко мне он уже облазил несколько форумов, но у него все равно осталось много вопросов. Отчаявшись он наконец подошел ко мне...мне…
 
{{Начало цитаты}}
В ходе противостояния греков и троянцев была разработана и впервые реализована операция по дезинформации противника. Греки построили огромного деревянного коня, в котором разместили небольшое войско, и поставили его под ворота Трои. В итоге сооружение было перемещено в город и греки одержали победу. Концепция "«троянского коня"» оказалась настолько действенной, что до сих пор используется всеми разведывательными службами мира. Кроме того, этот метод широко используется хакерами в целях получения нужной информации о своих "«жертвах"».
{{Конец цитаты}}
 
==== Построение серверной части ====
На руках у него уже была серверная часть программы, которая позволяла манипулировать удаленной файловой системой (как потом оказалось, ему ее презентовал <tt>Invalid</tt>). Давайте на нее посмотрим...посмотрим…
 
<code>require 'socket'
Строка 3242 ⟶ 3142 :
end</code>
 
Программка конечно хорошая, но сдается мне, что она не работает. Первое на что следует обратить внимание - — это закрытие соединения после выполнения каждой команды. При работе с этим сервером через [[w:Telnet|telnet]] будут возникать определенные проблемы. Давайте предельно упростим специализированный код этой программы и посмотрим на структуру клиент-серверного приложения.
 
<code><u>require 'socket'</u>
Строка 3280 ⟶ 3180 :
 
После небольших манипуляций с кодом стал виден базовый каркас сервера. Он состоит из следующих блоков:
* <code>require <u>'socket'</u></code>
Для того, чтобы работать с классом <tt>TCPServer</tt> (и не только с ним) необходимо подключить библиотеку <tt>socket</tt>.
* <code>TCPServer.open(<u>'localhost',3000</u>){ |server| ... }</code>
Этот программный код создает сервер, который будет прослушивать порт <tt>3000</tt>. В качестве порта может использоваться любой другой (например, <tt>31337</tt>). Менять имя хоста (<tt>localhost</tt>) не нужно, если только у Вас не несколько сетевых интерфейсов. Если у Вас их все таки несколько, то ничего по поводу смены хоста Вам объяснять не надо. Вы и так все, скорее всего, знаете ...
* <code>if (<u>session = server.accept</u>) ... end</code>
При помощи такой нехитрой комбинации ловится соединение с сервером. Обратите внимание, что в примере студента использовалась конструкция <code>while</code>. Ее пришлось упразднить, т.к.так как большой необходимости в ней не было. Как только вызов <code>server.accept</code> возвращает значение, то это означает, что к серверу подсоединился клиент. В переменную <code>session</code> записывается указатель на соединение. С ним-то мы и будем работать дальше...дальше…
* <code>cmd, arg = <u>*</u>session.gets.chomp.split</code>
Данный код интересен тем, что в программе студента для его реализации задействовано аж три строчки. И все лишь потому, что он не знал, что такое оператор <tt>*</tt> (звездочка). Этот оператор преобразует массив в список параметров. Тем самым он освобождает нас от нуждого присваивания. Сам же код получает от клиента строку, которая интерпретируется им как "«команда и аргумент, разделенные пробелом"». Обратите внимание, что работа идет с переменной <code>session</code>, а не <code>server</code>.
* <code>case <u>cmd</u> ... end</code>
Ветвление <code>case</code> необходимо для обработки команд. В зависимости от значения переменной <code>cmd</code> будут выполнены те или иные действия.
* Остальной код описывать не буду, так как каркасом клиент-серверного приложения он не является, а реализует функциональность конкретного приложения.
Строка 3294 ⟶ 3194 :
{{info|Для того, чтобы прекратить работу сервера необходимо нажать комбинацию клавиш <tt>Ctrl+C</tt> или <tt>Ctrl+Break</tt>. Команду отключения данный сервер не поддерживает. Команда <tt>shutdown</tt> относится к клиенту}}
 
Несмотря на то, что данный сервер вполне рабочий, у него есть один существенный недостаток - — он не работает для нескольких клиентов. Для того, чтобы это реализовать необходимо обрабатывать каждое соединение с клиентом в отдельном потоке. Кто изучал мультипроцессорное программирование, тот понимает о чем речь. Вот только не стоит сразу кидаться в книжный магазин за необходимой литературой. В составе дистрибутива Ruby уже есть замечательная библиотека <tt>gserver</tt>, которая как раз и занимается тем, что реализует обработку запросов клиентов в отдельных потоках. Для демонстрации ее работы перепишем предыдущую программу под <tt>gserver</tt>.
 
<code><u>require 'gserver'</u>
Строка 3334 ⟶ 3234 :
* <code>require '<u>gserver</u>'</code>
Вместо библиотеки <tt>socket</tt> мы подключаем библиотеку <tt>gserver</tt>. Для нас это означает то, что работать с классом <tt>TCPServer</tt> напрямую мы не будем. Использовать мы будем класс <tt>GServer</tt>.
* <code>class <u>Tpo9H < GServer</u></code>
Этот код создает класс Tpo9H, который наследует функциональность от класса GServer. Можно было бы конечно просто расширить класс GServer, но так хотелось "«хакерское"» название класса, что я не смог удержаться и произвел наследование.
* <code>def serve( <u>session</u> )</code>
Метод <tt>serve</tt> (название какое-то незаконченное...незаконченное… но на англ. означает "«обслуживать"») используется классом <tt>GServer</tt> как обработчик сессии с клиентом. При выходе из метода, сессия автоматически закрывается. Обратите внимание, что переменная <tt>session</tt> является параметром метода. Весь код обработки сессии взят из предыдущей программы без изменений.
* <code>tpo9H = Tpo9H.new( <u>31337</u> )</code>
Создается экземпляр класса Tpo9H. В качестве порта для "«прослушки"» указан <tt>31337</tt>. Почему не <tt>3000</tt>? Потому, что захотелось! При желании можете его сменить на любой другой.
* <code>tpo9H<u>.audit = true</u></code>
Если пропустить эту строчку, то сервер будет работать молча. Настолько молча, что Вы не сможете понять работает он или нет? Надпись об удачном запуске сервера будет выглядеть примерно так:
[Mon Oct 23 23:33:12 2006] Tpo9H 127.0.0.1:3000 start
Если она не появилась, то появится какая-то другая, которая известит Вас об синтаксической ошибке, которую Вы допустили при переписывании кода.
* <code>tpo9H<u>.start</u></code>
Этой командой мы стартуем сервер.
* <code>tpo9H<u>.join</u></code>
Класс <tt>GServer</tt> (и его наследники) прослушивает порт в фоновом режиме. Но если программа завершается, то завершатся и все нити (Thread'sThread’s). Поэтому надо чем-то занять программу на время работы сервера. Вот и было решено ожидать завершения прослушивающей нити сервера. Эта команда останавливает выполнение программы (за исключением нитей в фоне) до окончания прослушивания экземпляром созданного класса.
 
==== Построение клиентской части ====
Как мы уже видели выше, серверная часть полностью определяет функциональность всего клиент-серверного приложения. Клиентская часть же лишь принимает от пользователя запросы, пересылает их серверу и получает ответ, который передает пользователю.
 
Хотя Geka и имел в своем распоряжении клиентскую программу, ее мы рассматривать не будем, т.к.так как с тех пор его сервер претерпел некоторые изменения и прежняя клиентская часть не может работать с нынешней серверной. Мы напишем новую клиенскую часть "«с нуля"».
 
Для соединения с сервером по протоколу [[w:TCP/IP|TCP/IP]] используется класс <tt>TCPSocket</tt> из библиотеки <tt>socket</tt>. Для того, чтобы наша задача была конкретней, мы заставим сервер (при помощи нашего клиента) выполнить следующие команды: <tt>ls</tt>, <tt>cd ..</tt>, <tt>ls</tt> и <tt>shutdown</tt>. Вывод результата выполнения этих команд мы будем осуществлять на экран, но пользователь не получит возможности изменять эту последовательность действий (кроме как исправив программу). Он увидит только результат. Логика здесь следующая: зачем заставлять пользователя вводить команды, если это может делать программа? Если же пользователю нужна другая последовательность команд, то пусть использует telnet или правит клиентскую часть под свои нужды.
Строка 3370 ⟶ 3270 :
}</code>
 
Все замечательно, но программа не работает. Не вся, конечно...конечно… Она выводит приглашение к диалогу и все, дальше виснет. Это связано с тем, что используется метод .read, который считывает весь поток целиком, пока не встретит символ EOF. Его, то наш сервер как раз и не передает. Не будем пока спешить и править сервер, а применим один нечестный прием: будем использовать не метод <code>.read</code>, а метод <code>.sysread( <u>n</u> )</code>. Почему прием нечестный? Да все потому, что метод <code>.sysread( n )</code> считывает <tt>n</tt>-первых сиволов из потока. Так как мы не знаем сколько нам надо считать символов, то мы зададим в качестве <tt>n</tt> очень большое число. Например, <tt>5000</tt>. Если символов в потоке меньше, чем <tt>5000</tt>, то <code>.sysread( n )</code> считает столько, сколько есть. Эту особенность мы и используем. Разве можно сказать, что такой прием честный?
 
<code>require 'socket'
Строка 3384 ⟶ 3284 :
}</code>
 
Уже лучше...лучше… по крайней мере программа работает. Но давайте поразмышляем над ситуацией, которая произошла с методом <code>.read</code>. Если немного подправить сервер и выдавать после каждой передачи этот символ, то программа с <code>.read</code> могла бы с успехом работать. Какова здесь мораль? А мораль в том, что для успешной работы необходимо с сервера передавать сигнал, который означал бы "«последняя строка, которую я передаю клиенту"». Чтобы клиент не пытался читать данные с сервера, а начинал их передачу. Вполне естественно, что добавление такого сигнала означает модификацию сервера. В качестве сигнала последней строки мы будем использовать строку <tt>+ОК</tt>. Почему именно такую? Просто видел ее где-то, вот и решил использовать. Если хотите, то можете использовать свою строку. Только не забудьте об этом, когда будете писать программу-клиент. Вот модифицированный сервер:
 
<code>require 'gserver'
Строка 3424 ⟶ 3324 :
</code>
 
Была добавлена лишь одна команда (хотя, самые внимательные могут заметить, что еще и метод <code>.print</code> был заменен на <code>.puts</code>) - — <code>session.puts <u>"+OK"</u></code>. Она будет выполнятся после каждой передачи данных от сервера к клиенту. Тем самым мы будем извещать клиента о том, что передача завершается. Теперь займемся переписыванием клиента. Необходимо исправить код там, где происходит чтение, чтобы он учитывал строки <tt>+OK</tt>.
 
<code>require 'socket'
Строка 3456 ⟶ 3356 :
При помощи команды <code>str[/^\+OK/]</code> мы проверяем наличие строки <tt>+OK</tt> в переменной <tt>str</tt>. Если проверка прошла успешно, то происходит выход из бесконечного цикла. Если нет, то продолжается получение данных и вывод их на экран.
* <code>str[/^\+OK/] ? break : puts( str )</code>
Это всего лишь иная запись кода: <code>if str[/^\+OK/] then ... else ... end</code>. Зато более короткая...короткая…
* <code>while <u>!(str = client.gets.chomp)[/^\+OK/]</u> do ... end</code>
Условный оператор и бесконечный цикл были объедененыобъединены в цикл с предусловием. Условия выхода тоже самое, но оно совмещено с чтением и присваиванием. Выглядит жутковато, но это вполне работоспособный код.
 
Из всех предложенных вариантов вы вольны выбирать любой...любой… но мне не нравится вообще весь клиент. Как-то он сильно разросся и теперь выглядит монстрозно. Конечно же, есть возможность загнать чтение в отдельный метод, но мы этого делать не будем. Хотя...Хотя… где наша не пропадала?! Давайте вынесем код отсылки команды и получения ответа в отдельный код. Естественно, что это будет метод для класса <tt>TCPSocket</tt> (который мы будем расширять). Назовем мы его <code>.cmd</code>.
 
<code>class TCPSocket
Строка 3558 ⟶ 3458 :
=== Как создать сетевой блокнот? ===
Идея написать подобную программу появилась после прочтения статьи [http://doci.nnm.ru/php_forall_/22.11.2006/sozdaem_svoj_onlajn_bloknot/ Создаем свой online-блокнот]. Продемонстрированная там программа предельно проста, но на ее примере очень удобно показать работу с [[w:ERB|ERB]]. А учитывая тот факт, что ERB используется в составе инструментария [[w:Ruby on Rails|Руби на Рельсах]], то ценность этой статьи становится очевидной.
 
==== Первое приближение ====
В первом приближении мы попытаемся реализовать ту же самую функциональность, что и описана в статье. Вот только [[w:PHP|PHP]] подразумевает наличие веб-сервера, который будет заниматься интерпретацией его команд. В нашем же примере мы самостоятельно поднимем веб-сервер (написанный на Ruby), чтобы не заморачиваться с настройкой стороннего.
 
Смысл всей программы следующий: надо создать страницу с окном ввода и кнопкой "«Сохранить"», при нажатии на которую происходит сохранение текста из окна ввода в файл <tt>notepad.txt</tt>. Ввод осуществляется через [[w:браузер|браузер]] по адресу http://localhost:8080.
 
{{info|Сервер будем запускать на порт 8080, так как стандартный для веб-серверов порт 80 у меня занял Skype. При желании, порт можно легко поменять}}
Строка 3589 ⟶ 3490 :
: Процедурному (как и любому другому) сервлету передается в блок две переменные. Переменная <tt>req</tt> содержит информацию о запросе (какой браузер запрашивает, какие переменные передаются и так далее), а при помощи переменной <tt>resp</tt> формируется ответ (какой тип информации передается, информация для отображения и так далее).
* <code>... if <u>req.query["text"]</u></code>
: Постфиксная запись условного оператора <tt>if</tt>. Блок, перед <tt>if</tt> будет выполняться только в том случае, когда сервлету будет передаваться переменная <tt>text</tt>. Метод <tt>.query</tt> возвращает ассоциативный массив в виде <tt>{имя_переменной => значение}</tt>.
* <code>File.open(<u>'notepad.txt'</u>,<u>'w'</u>){ |f| f.write( <u>req.query["text"]</u> ) }</code>
: Открываем файл <tt>notepad.txt</tt> для записи и пишем туда значение переменной <tt>text</tt> (передается сервлету в теле запроса).
Строка 3612 ⟶ 3513 :
{{Внимание|Обработка кода внутри тегов ERB идет только во время обработки шаблона (вызова метода <tt>.result</tt>)}}
 
Не знаю почему, но ERB для меня это "«PHP, только на Ruby"». Эта фраза обладает столь магическим свойством, что после нее собеседнику все становится понятным. Вот и вам советую воспринимать ERB как "«PHP на Ruby"».
 
{{info|Библиотека WEBrick поддерживает PHP-скрипты благодаря [[w:CGI|CGI]]-сервлету}}
Строка 3640 ⟶ 3541 :
Создаем объект ERB и передаем туда подготовленный ERB-шаблон. Обрабатываем его методом <tt>.result</tt> и результирую строку передаем методу <tt>.body=</tt>, который подставляет ее в качестве ответа на запрос.
 
И что мы получаем в результате? Подключили "«лишнюю"» библиотеку и создали "«лишнюю"» переменную? Не будем спешить с выводами. Использование библиотеки ERB позволяет вынести шаблон во внешний файл. Тем самым мы очищаем Ruby-код от HTML-кода.
 
{{Начало цитаты}}«Кесарю — кесарево!» (с) [[w:Иисус Христос|Иешуа Ганоций]]{{Конец цитаты}}
 
{{Начало цитаты}}"Кесарю - кесарево!" (с) [[w:Иисус Христос|Иешуа Ганоций]]{{Конец цитаты}}
==== Выносим ERB-шаблон во внешний файл ====
ERB-шаблон существенно портит красоту нашего Ruby-кода. Поэтому, решение вынести шаблон во внешний файл вполне оправдано. Тем более, что это позволит нам править ERB-шаблон отдельно от программы. Более того, внесенные в шаблон изменения будут вступать в силу без перезагрузки сервера.
Строка 3670 ⟶ 3572 :
server.start</code>
 
Вот, уже другое дело! Можно было бы этим и ограничиться, если бы в библиотеке <tt>WEBrick</tt> отсутствовал бы ERB-сервлет...сервлет… Но он есть!
 
==== ERB-шаблон превращается в ERB-сервлет ====
Строка 3692 ⟶ 3594 :
Как можно заметить, в самое начало шаблона был добавлен тег, который осуществляет сохранение содержимого переменной <tt>text</tt> в файл <tt>notepad.txt</tt>. Код тега был перенесен из программы практически один к одному. За одним только исключением: к переменной <tt>text</tt> мы теперь обращаемся через переменную <tt>query</tt>, а не через <tt>req.query</tt>. Вот и все отличие!
 
На этом все...все… из чего состоит наша программа теперь?
* <tt>notepad.rb</tt>. Программа-сервер. Назвать файл можно на свое усмотрение. Главное, чтобы работал. Содержит логику, которая осуществляет конфигурирование и запуск сервера.
* <tt>index.html</tt>. ERB-шаблон. В нем содержится вся логика программы, кроме реализованной в программе-сервере.
Строка 3703 ⟶ 3605 :
 
=== Как пропинговать компьютер в сети? ===
 
Открываем новую серию статей, которые будут рассказывать про использование встроенных библиотек Ruby. Первая статья будет посвящена написанию утилиты <tt>ping</tt> (в очень упрощенной форме). Смотрим в стантартную библиотеку и обнаруживаем файлик <tt>ping.rb</tt>. Смотрим в него и обнаруживаем метод <code>pingecho</code>. Метод используется следующим образом:
<source lang=ruby>require 'ping'
Строка 3726 ⟶ 3627 :
 
=== Простейший датчик работы службы ===
Летним воскресным утром мне захотелось сделать что-то приятное и красивое… Я написал письмо в конференцию о моих светлых идеях, но не смог его отправить. [[w:SMTP|SMTP]]-сервер безбожно висел. И решило мое больное воображение написать программку, которая документировала бы подобные ситуации. В общем мне нужен был простейший документ (лог работы службы), который бы доказывал, что наши сетевые админы зря едят свой хлеб (а на самом деле я добрый). Для начала я определился с информацией, которая мне была нужна. А именно:
 
Летним воскресным утром мне захотелось сделать что-то приятное и красивое... Я написал письмо в конференцию о моих светлых идеях, но не смог его отправить. [[w:SMTP|SMTP]]-сервер безбожно висел. И решило мое больное воображение написать программку, которая документировала бы подобные ситуации. В общем мне нужен был простейший документ (лог работы службы), который бы доказывал, что наши сетевые админы зря едят свой хлеб (а на самом деле я добрый). Для начала я определился с информацией, которая мне была нужна. А именно:
* текущее время;
* время отклика;
* баннер службы.
 
Ее я решил писать в логи следующим образом...образом… Каждый день будет создаваться файл лога и каждый час в него будет писаться информация о работе службы. В качестве планировщика заданий использовался виндовый [http://www.nncron.ru/ Cron], который запускал мою программу в нужное время. Для начала я написал программу, которая соединяется со службой SMTP и получает от него баннер:
<source lang=ruby>require 'socket'
 
Строка 3744 ⟶ 3644 :
f.puts( "#{sprintf( '%02d',Time.now.hour )} | #{end_time-begin_time} | #{request}" )
}</source>
Как и следовало ожидать, программа не работала. Она подвисала и не хотела ничего писать в файл. Висела она на строчке:
<source lang=ruby>request = t.gets.chomp</source>
Чтобы разобраться с проблемой, пришлось читать книжку. Слава богу, что под рукой оказалась книга [http://books.dore.ru/bs/f1bid1824.html TCP/IP. Учебный курс]. В ней на с.345 черным по серому была начертана схема взаимодействия SMTP протокола. Как оказалось, чтобы получить баннер от службы, надо послать команду <tt>NOOP</tt>. Переписываем наш фрагмент программы.
Строка 3759 ⟶ 3659 :
f.puts( "#{sprintf( '%02d',Time.now.hour )} | #{end_time-begin_time} | #{request}" )
}</code>
Великолепно! Программа работает...работает… но иногда зависает. И тогда меня посетила еще одна мысль: на соединение отводить всего одну секунду (не уложился  — сам дурак). Если соединение зависало, то в файл записывалось <tt>timeout</tt>. Чтобы "«отводить время на выполнение операции"» нужно задействовать библиотеку <tt>timeout</tt>. Она у меня входила в состав дистрибутива. Итак, переписываем нашу программу:
<code><u>require 'timeout'</u>
require 'socket'
Строка 3779 ⟶ 3679 :
f.puts( "#{sprintf( '%02d',Time.now.hour )} | #{beachmark} | #{request}" )
}</code>
Все бы хорошо, но вот <tt>beachmark</tt> хотелось бы считать "«по-взрослому"», а именно при помощи пакета <tt>beachmark</tt>. И снова переписываем код:
<code>require 'timeout'
require 'socket'
Строка 3805 ⟶ 3705 :
user system total ( real )
</tt>
Нам нужен только <tt>real</tt>. Все остальное  — для более детального анализа. Поэтому немного доработаем результат блока <tt>measure</tt>:
<source lang=ruby>beachmark = Benchmark.measure{
timeout(1){
Строка 3842 ⟶ 3742 :
 
=== Датчик обновления сайта ===
Ни для кого не секрет, что [[w:Сетевой администратор|админы]] — это жутко ленивый народ. Но начальство, как назло, не хочет платить деньги за просто так. Ему нужны отчеты! Представляю на Ваш суд датчик обновления новостной ленты сайта. Для начала следует как следует поставить себе задачу:
 
Ни для кого не секрет, что [[w:Сетевой администратор|админы]] — это жутко ленивый народ. Но начальство, как назло, не хочет платить деньги за просто так. Ему нужны отчеты! Представляю на Ваш суд датчик обновления новостной ленты сайта. Для начала следует как следует поставить себе задачу:
* Есть сайт и админ его частенько обновляет.
* Будем рассматривать только случай добавления новостей. Все остальные разделы можно контролировать аналогично.
Строка 3850 ⟶ 3749 :
* Новости располагаются на первой странице сайта.
 
Итак, для чего нам нужен датчик? Допустим Вы админ сайта и постоянно добавляете новости. По окончании недели (месяца, года, столетия, ...) от Вас требуют отчет о проделанной работе. Вам приходится заходить на сайт и смотреть те новости, которые Вы добавили за последний период. Муторно и неэффективно. Намного приятнее постоянно вести записи о добавленных новостях (при помощи программы, конечно) и по завершении периода просто сделать соответствующую выборку.
 
Итак, немного об алгоритме программы...программы… Обычно новостей на главной странице строго определенное количество. На нашем тестовом сайте из ровно пять. У нас есть файл в котором мы храним дату добавления новости и заголовок новости. Разделитель у нас может быть произвольным, но в качестве примера будет использован набор символов <code>' ^_^ '</code>. Вообще для данной задачи даже разделитель не очень-то и нужен (дата состоит из строго определенного количества символов и записывается в строго определенном формате)... но универсальность превыше всего!
 
Каждый раз при запуске программы, мы скачиваем заглавную страницу сайта и выдираем оттуда даты и заголовки новостей. Потом читаем файл с подобным же набором даных. Читаем весь файл, хотя можно читать только последние n-строк. Но мы будем создавать каждый месяц новый файл и поэтому особой загрузки памяти происходить не должно. Далее сравниваем эти два набора данных и с помощью пересечения и вычитания множеств мы получаем те данные, которых до сих пор нет в файле. Как раз эти данные мы и добавляем в конец файла. ВСЕ! Просто и лаконично. Теперь код, который выполняет поставленную задачу:
Строка 3868 ⟶ 3767 :
f.puts( ( from_inet - (from_file & from_inet) ).sort )
}</source>
В результате мы получаем файлы, которые могут быть с легкостью использованы для составления отчетов. Будут предложения по оптимизации  — пишите на форум.
 
=== Приемо-передача файлов ===
 
=== Как скачать HTML-страницу ===
Для скачивания [[w:HTML|HTML]]-страниц обычно используется [[w:HTTP|протокол HTTP]]. Адрес страницы задается в виде [[w:URL|URL]]. В Руби существует несколько способов скачать страницу по протоколу HTTP. Начнем с самого древнего — класса Net::HTTP (древнее только Socket'ыSocket’ы, но про них мы умолчим). Для определенности мы будем скачивать этот учебник и сохранять его в виде файла RubyBook.html.
 
<code>require 'net/http'
Строка 3894 ⟶ 3793 :
 
==== Запрос заголовка ====
Во время скачивания передается не только сама страница (<b>'''тело сообщения</b>''' или на англ. body), но и техническая информация (<b>'''заголовок</b>''' или на англ. head). Мы еще не раз будем свидетелями такого поведения в протоколах сети Интернет. В заголовке содержится информация об успешности запроса (код возврата), типе передаваемых данных, кодировке, HTTP-сервере и так далее. Для примера, мы будем производить HTTP-запрос и получать ответ только в виде заголовка. Запись заголовка в файл производить не будем, так как в реальной практике этот прием практически не используется. Сначала «потренируемся на кошках», то есть на классе Net::HTTP:
 
<code>require 'net/http'
Строка 3918 ⟶ 3817 :
p open( 'http://ru.wikibooks.org/wiki/Ruby' )<u>.meta</u></code>
 
Замена метода <code>.read</code> на метод <code>.meta</code> позволяет нам дотянуться до заголовка. Заголовок имеет вид ассоциативного массива (объект класса Hash), где ключем является имя параметра (по стандарту MIME), а значением — значение параметра. Читать одновременно заголовок и тело сообщения можно вот так:
 
<code>require 'open-uri'
Строка 3930 ⟶ 3829 :
 
==== Работа через прокси ====
Прокси-сервер — это сервер, перенаправляющий запросы другим серверам. Обычно таковой используется для скрытия истинного [[w:IP-адрес|IP-адресадреса]]а или для контроля за [[w:трафик|трафиктрафиком]]ом.
 
Необходимость HTTP-запроса через прокси возникает, когда соединение с целевым сервером напрямую невозможно (например, администратор сети посчитал, что соединение посредством шлюза дает ему слишком мало возможностей контроля за трафиком). Использование подобного запроса маловероятно, но необходимо знать о возможности посылки http-запроса через прокси. Начнем с класса Net::HTTP::Proxy:
Строка 3947 ⟶ 3846 :
p open( 'http://ru.wikibooks.org/wiki/Ruby',<u>:proxy => 'http://you.proxy.host:8808/'</u>).read</code>
 
Добавился второй параметр (ассоциативный массив с единственной парой) в вызове метода <code>open</code>. К слову сказать, во втором параметре можно указать множество параметров запроса (например, название браузера).
{{info|Реализация соединения через прокси у метода <code>open</code> более удобна, так как в зависимости от внешних факторов можно соединяться как через прокси, так и напрямую. В случае Net::HTTP и Net::HTTP::Proxy используются два разных класса и подобные трюки затруднительны (хотя и возможны).}}
 
=== Вам письмо или You have got mail ===
 
=== Запускаем свой веб-сервер ===
Для того, чтобы создать простейший [[w:Веб-сервер|веб-сервер]] не нужно реализовывать протокол [[w:HTTP|HTTP]] на уровне [[w:Сокет (программный интерфейс)|сокетов]]. Достаточно знать, какую библиотеку использовать. В нашем случае это будет библиотека [[w:WEBrick|WEBrick]] (http://www.webrick.org), которая уже включена в дистрибутив Руби. Возможности библиотеки настолько широкие, что для их описания потребуется создание отдельного учебника. Мы рассмотрим лишь часть из них, зато на реальном жизненном примере.
Строка 3967 ⟶ 3867 :
<code><nowiki><html><body><h1>Сервер работает!</h1></body></html></nowiki></code>
 
После обновления страницы на экране красовалась крупная надпись: "«Сервер работает!"» Все! Мы с вами написали свой первый веб-сервер.
 
{{Внимание|Для того, чтобы прекратить выполнение сервера нужно нажать комбинацию клавиш Ctrl+Break или закрыть окно терминала. Для данной реализации веб-сервера это единственный способ прекратить его работу}}
Строка 3975 ⟶ 3875 :
: Подключение библиотеки <tt>WEBrick</tt>. Ничего интересного и неожиданного здесь не произошло.
* <code>WEBrick::HTTPServer.new( <u>:DocumentRoot => 'public_html'</u>, <u>:Port => 8080</u> ).start</code>
:* Библиотека <tt>WEBrick</tt> реализована в виде модуля с одноименным названием, поэтому до любого класса приходится «дотягиваться» через префикс: <code>WEBrick::</code>.
:* <code>HTTPServer</code>  — это название класса, который используется для создания веб-серверов.
:* Метод <code>.new</code> создает экземпляр класса <tt>WEBrick::HTTPServer</tt>. Методу передается два параметра (на самом деле  — один ассоциативный массив с двумя парами):
::* <code>:DocumentRoot => '<u>public_html</u>'</code> указывает на то, что веб-сервер будет просматривать директорию <tt>./public_html</tt> для выдачи [[w:HTML|html]]-страниц (и не только) по запросу. Если этот параметр не указывать, то сервер просто не будет работать.
::* <code>:Port => <u>8080</u></code> задает порт <tt>8080</tt> для «прослушивания» сервером. Если этот параметр не указывать, то сервер будет прослушивать порт <tt>80</tt> (стандарт для веб-серверов). Но случилось так, что «слушать» <tt>80</tt>-порт он у меня отказался, так как там «обосновался» [[w:Skype|Skype]]. Вот и пришлось в срочном порядке придумывать новый. Ничего умнее, как продублировать число <tt>80</tt> мне в голову не пришло. Если фантазия у вас развита лучше, то придумайте любой другой порт.
Строка 4043 ⟶ 3943 :
Внутри тега <tt>FORM</tt> присутствует великое множество тегов <tt>INPUT</tt>, которые как раз и формируют данные. Каждый из тегов <tt>INPUT</tt> имеет два параметра: <tt>NAME</tt> и <tt>TYPE</tt>.
* <code>TYPE="<u>checkbox</u>"</code>
: Параметр <tt>TYPE</tt> указывает на тип формируемых данных. В данном случае это <tt>checkbox</tt> (переключатель). Он имеет два значения: "«on"» (включен) и "«off"» (выключен). Значение "«off"» обычно не передается. Получается своеобразный [[w:Булевский тип|булевский тип]].
* <code>NAME="<u>check_</u>01"</code>
: Все данные передаются в виде пары <code>имя=значение</code>. Параметром <tt>NAME</tt> задается имя. Значение же задается пользователем.
Строка 4108 ⟶ 4008 :
Начинаем разбираться с кодом веб-сервера:
* <code>bosses = { ... }</code>
: Создается ассоциативный массив, который будет использоваться при обработке данных. Ключем является имя «переключателя», а значением  — строка, на которую надо этот ключ заменить. Путем такой замены мы будем формировать результат.
* <code>require 'webrick'</code>
: Подключаем библиотеку <tt>WEBrick</tt>. По-хорошему ее надо было подключать перед инициализацией ассоциативного массива. Но не хотелось разбивать на части код, относящийся к реализации веб-сервера.
Строка 4128 ⟶ 4028 :
 
 
Вот почти такую программу я и презентовал секретарше Анюте. «Почти» потому, что фамилии должны идти строго в определенном порядке (чего нельзя добиться в ассоциативном массиве), но для того, чтобы посмотреть реализацию веб-сервера  — продемонстрированной версии программы должно быть достаточно. Смело меняйте код и создавайте свои веб-сервера!
 
==== Сервлетки в рубиновом соусе ====
Строка 4137 ⟶ 4037 :
: Реализует взаимосвязь запроса с <tt>CGI</tt>-приложением, то есть считывается первая строка и вытаскивается оттуда путь к интерпретатору. Далее производится запуск интерпретатора, которому параметром передается вызванный файл. Строка для <tt>CGI</tt>-приложений на Ruby будет выглядеть приемерно так:
::* <code>#!<u>/usr/bin/ruby</u></code>
::: для операционных систем семейства Unix
::* <code>#!<u>c:/ruby/bin/ruby.exe</u></code>
::: для операционных систем семейства Windows
Строка 4169 ⟶ 4069 :
exec(ENV["SCRIPT_FILENAME"])
end</source>
: Немного поясню. Первая строчка -- — это программа, которая записывает нижеследующий код в папку с библиотеками.
: Функциональность CGI-сервлета описана в классе WEBrick::HTTPServlet::CGIHandler
* '''ERB-сервлет'''
Строка 4214 ⟶ 4114 :
;[[/Фольклор/]]:Народное творчество любителей Руби
;[[/Избранное с RubyNews/]]:К разграблению. Полезные статьи перемещаются в основную часть учебника, затем это приложение удалим.
;[[/Идеология/]]: программирования вообще и на Руби в частности
 
== Дальнейшее чтение ==
 
=== Русскоязычные ресурсы ===
* [http://ror.ru Все о Ruby on Rails] Рускоязычный ресурс по Ruby.
* [http://ruby.inuse.ru RubyInUse] Территория общения русскоязычных рубистов.
* [http://rubyclub.com.ua/ Форум Ruby on rails]. Русскоязычный форум написанный и посвященный Ruby on Rails, здесь всегда отвечают на ваши вопросы, для зарегистрированных доступна тематическая PDF библиотека.
* [http://acm.mipt.ru/twiki/bin/view/Ruby/WebHome Учебные материалы МФТИ]. Коллекция учебных материалов по Ruby на сайте [[w:МФТИ|МФТИ]]
 
=== Русскоязычная литература ===
* [http://www.shokhirev.com/mikhail/ruby/ltp/title.html Learn To Program]. Перевод Михаила В. Шохирева.
* [http://www.linuxshare.ru/docs/devel/languages/ruby/ruby.html Ruby  — руководство пользователя]. Перевод Александра Мячкова.
* [http://www.ruby.linux.by/man-ruby_lections.html Конспект лекций по высокоуровневому программированию]. Шипиев Р. Н.
* [http://web.archive.org/web/20071008161801/http://sabanin.ru/wp-content/articles/Rolling_with_Ruby_on_Rails_rus_translation.pdf Rolling with Ruby on Rails]. Перевод Андрея Горбатова (pdf).
* [http://ruby.rostovlinux.ru/rubylinks.html Самоучитель Ruby]. Параллельный проект по написанию учебника.
* [http://www.main.msiu.ru/small-portals/programmers/library/ruby/faq/faq.html ЧАсто задаваемые ВОпросы]. (требуется авторизация) FAQ по Ruby.
* [http://www3.msiu.ru/~roganova/First_A_2005-2006/ruby_notes.pdf Программирование на Ruby]. Учебное пособие по языку Ruby, Роганова Н. А., Тузов А. С.
* [http://www3.msiu.ru/~roganova/First_A_2005-2006/index.html Лекции по программированию]. Роганова Н. А.
 
=== Иноязычная литература ===
* [http://poignantguide.net/ruby/ ''Why’s (Poignant) Guide to Ruby'']{{ref-en}}  — эта книга достойна чтения, ''даже если Вам не нужно знание Руби''. Просто шедевр. Распространяется бесплатно. <small>Там ещё богатая подсветка текста; эх, нам бы такую.</small>. [[Why's Poignant Guide To Ruby RUS|Частичный перевод здесь]].
* ''Programming Ruby'' Дэйва Томаса{{ref-en}}. Многие пытались, но не перевели на русский. Первая редакция книги содержится в пакете [http://rubyinstaller.rubyforge.org/wiki/wiki.pl «Установка за один щелчок»] для Windows. Владельцы других [[w:Операционная система|ОС]] смогут найти её в Сети (например, в виде [http://www.rubycentral.com/book/ набора вебстраниц]). Вторую редакцию книги можно купить на [http://www.amazon.com/gp/product/0974514055/sr=8-1/qid=1148659457/ref=pd_bbs_1/102-3466370-6596946?%5Fencoding=UTF8 Amazon.com] в бумажном или электронном виде.
 
* [http://ruby.on-page.net Ruby.on-page.net]{{ref-en}}  — самый простой справочник по Ruby
* [http://www.meshplex.org/wiki/Ruby/Ruby_on_Rails_programming_tutorials Full Ruby on Rails Tutorial]