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

2376 байт добавлено ,  5 месяцев назад
м
<source> -> <syntaxhighlight> (phab:T237267)
м (<source> -> <syntaxhighlight> (phab:T237267))
 
=== Первая программа ===
Откройте редактор и напишите программу. По традиции первая программа должна просто выводить приветствие миру:
<sourcesyntaxhighlight lang="ruby">
п "Привет, Мир!"
вывстр "Привет, Мир!"
</syntaxhighlight>
</source>
Сохраните её в файл <code>привет_мир.рб</code> и запустите. На экране будет красоваться надпись:
"Привет, Мир!"
 
В Ruby знаком начала комментария служит <code>[[w:Октоторп|#]]</code>. Все, что между ним и концом строки пропускается. Пример:
<sourcesyntaxhighlight lang="ruby">вывстр 2+2 # это комментарий
вывстр "Привет!" # тоже комментарий</sourcesyntaxhighlight>
 
Результат иллюстрируемого кода будет располагаться после последовательности <code>#-></code>. Пример:
<sourcesyntaxhighlight lang="ruby">puts 2+2 #-> 4
puts "Привет" #-> Привет</sourcesyntaxhighlight>
 
=== Вывод на экран ===
 
Примеры вывода на экран:
<code><sourcesyntaxhighlight lang="ruby">
puts [1,2,3,4] #-> 1\n2\n3\n4
# \n означает перевод строки-возврат каретки
puts 5 #-> 5
p 5 #-> 5
</sourcesyntaxhighlight></code>
 
Как видно из примера, результаты во время вывода строк и массивов существенно различаются. Если вас не смущают кавычки в результате вывода, то смело используйте <code>p</code>, если смущают, то <code>puts</code>.
{{info|Иногда возникает ситуация, когда при попытке вывода на экран русскоязычной строки при помощи метода <code>p</code> выводится непонятный код вида
<code><nowiki>"\323\367\350\362\345 \320\363\341\350!"</nowiki></code>
Чтобы избежать таких ситуаций следует поместить в начало программы следующий программный код:<code><sourcesyntaxhighlight lang="ruby">$KCODE = "utf-8"</sourcesyntaxhighlight></code>
Кодировка <tt>utf-8</tt> используется в <tt>SciTE</tt>. При использовании других редакторов, может потребоваться смена кодировки на соответствующую (зависит от редактора и операционной системы)}}
 
 
Примеры переменных:
<sourcesyntaxhighlight lang="ruby">maccuB
nepemeHHa9
gpyra9_nepemeHHa9
3JIemeHT # неправильное имя. Начинается с цифры
3JIemeHT # а вот как можно
__ # немного странное, но корректное имя переменной</sourcesyntaxhighlight>
 
[[w:Переменная (программирование)|Переменная]] может иметь имя не только латинское, но и русское. Для этого, правда, требуется, чтобы весь текст программы был написан в кодировке UTF-8, а интерпретатор запускался с параметром -KU
 
[[w:Присваивание (программирование)|Присвоение]] делается знаком равенства (<code>=</code>), вот так:
<sourcesyntaxhighlight lang="ruby">maccuB = [1,2,3,4]
nepemeHHa9 = maccuB + [1,2]
gpyra9_nepemeHHa9 = nepemeHHa9 - maccuB</sourcesyntaxhighlight>
 
То, что справа от <code>=</code>, прежде чем стать значением переменной, обычно полностью вычисляется. Наша <code>nepemeHHa9</code> будет содержать массив <code>[1, 2, 3, 4, 1, 2]</code>, ибо именно таков результат действия <code>maccuB + [1, 2]</code>. Плюс (<code>+</code>) с массивами поступает именно так: прицепляет второй массив в хвост первого.
==== Переменные '''указывают''' на объект ====
Фокус-покус:
<sourcesyntaxhighlight lang="ruby">nogpyra = "Даша"
k_HAM_B_rocTu_ugeT = nogpyra
puts nogpyra #-> "Даша", разумеется
k_HAM_B_rocTu_ugeT[0] = "М" # меняем первую (номер ноль) букву у переменной-строки
puts nogpyra #-> "Маша"
# На первый взгляд, странно и неожиданно</sourcesyntaxhighlight>
 
Значение, возвращаемое первой переменной изменилось потому, что
 
Чтобы <code>nogpyra</code> наша осталась <code>"Даша"</code>, надо в переменную занести её [[w:клонирование|клон]]:
<sourcesyntaxhighlight lang="ruby">nogpyra = "Даша"
k_HAM_B_rocTu_ugeT = nogpyra.clone
k_HAM_B_rocTu_ugeT[0] = "М"
# Но изменили мы лишь клон. Дома в сохранности сидит настоящая:
puts nogpyra #-> "Даша"</sourcesyntaxhighlight>
 
{{info|Можно создавать копии объектов ещё методом <code>.dup</code>. Разницу между ними Вы потом поймёте}}
 
Для безвредного присвоения новых значений переменным их редко приходится клонировать, ибо большинство методов делают это и так. Даже если просто присвоите переменной новое значение, Руби создаст объект с новым значением и поместит в (уже существующую) переменную ссылку на тот объект:
<sourcesyntaxhighlight lang="ruby">#...
k_HAM_B_rocTu_ugeT = "Аристарх"; # Создаётся новый объект, переменная переводится на него
p nogpyra #-> "Даша"</sourcesyntaxhighlight>
 
== Типы данных ==
=== Числа ===
[[w:Число|Числа]] в Ruby выглядят так:
<sourcesyntaxhighlight lang="ruby">5 # целое число
-12 # отрицательное целое число
4.5 # число с плавающей точкой
076 # восьмеричное число
0b010 # двоичное число
0x89 # шестнадцатиричное число</sourcesyntaxhighlight>
 
Не будем пока углубляться и мельком взглянем на другие типы данных.
 
Массив лучше всего вообразить как гусеницу и притом поезд с лапками-запятыми вместо колёс.
<sourcesyntaxhighlight lang="ruby">[1, 0, 740, 14, 88] # целочисленный массив
['a',"й", "6",'Br', "Это массив строк, о них Вы скоро узнаете"]
[[1,2],[3,4]] # двумерный целочисленный массив.
["1й элемент смешанного массива", "7.343", [4, "вепрь"],
[3, 67, 4326, 12, 3781357, 84221, "строка делает этот подмассив смешанным, но это не беда"]]
maccuB=["Этот массив пойдёт в переменную maccuB", "Як-цуп-цоп, парви каридула"]</sourcesyntaxhighlight>
 
'''Ползёт он всегда влево'''; на левом же конце его локомотив — первый элемент. Первый потому, что элементы упорядочены. Если знаете порядковый номер элемента, то легко получить его значение:
<sourcesyntaxhighlight lang="ruby">maccuB[1] #=> "Як-цуп-цоп, парви каридула"</sourcesyntaxhighlight>
В мире поездов-гусениц счёт вагонов начинается с локомотива, а не со следующего за ним вагона. Таким образом локомотив — это как бы нулевой вагон.
<sourcesyntaxhighlight lang="ruby">maccuB[0] #=> "Этот массив пойдёт в переменную maccuB"</sourcesyntaxhighlight>
 
Массивы кажутся странными с лапками, но частенько бывают полезны при написании компьютерных программ. Возможно, вы даже поймёте, почему он ползёт всегда влево.
 
Строки начинаются и заканчиваются <code>"</code> (лапками) или <code>'</code> (типографским апострофом). Пример:
<sourcesyntaxhighlight lang=ruby>"мама мыла раму" # строка в надёжных лапках
'рама сопротивлялась' # строка в апострофах</sourcesyntaxhighlight>
 
Строки подобны массивам символов, поэтому их часто преобразуют к массивам, чтобы использовать богатый набор методов, а потом результат делают строкой.
=== Ассоциативные массивы ===
[[w:Ассоциативный массив|Ассоциативные массивы]] подобны массивам [[w:упорядоченная пара|упорядоченных пар]]. Работают они подобно словарям: фигурная скобка символизирует боковой вид на открытую книгу, а стрелка <code>=></code> покажет читателю связь каждого одного с чем-то другим. Вторая фигурная скобка говорит, что пора закрывать книгу.
<sourcesyntaxhighlight lang=ruby>{ "мама" => "мыла раму", 807 => "Это число улыбается!"}</sourcesyntaxhighlight>
Но можно и без фигурных скобок, одной стрелкой:
<sourcesyntaxhighlight lang=ruby> "npeBeg" => "MegBeg"</sourcesyntaxhighlight>
Например:
<sourcesyntaxhighlight lang=ruby>puts maccuB["мама"] #-> мыла раму
puts maccuB["807"] #-> nil
puts maccuB[807] #-> Это число улыбается!
puts maccuB[1] #-> nil
puts maccuB["npeBeg"]#-> MegBeg</sourcesyntaxhighlight>
 
Ассоциативные массивы оставляют возможность хранения данных разного типа только в ассоциативном виде.
Чтобы было удобней получать подмассив или подстроку, был введен простенький тип данных — диапазон (класс <code>Range</code>). Диапазон формируется тремя элементами: начало, конец и тип протяженности (символ <code>..</code> или <code>...</code>). Начало и конец должны быть одного типа данных (одного класса) и быть '''перечислимыми''', что значит, иметь метод <code>.succ</code>. Пример диапазонов:
 
<sourcesyntaxhighlight lang=ruby>'a'..'z'
'a'...'z' # то же, что и 'a'..'y'
1..100
1...100 # то же, что и 1..99</sourcesyntaxhighlight>
 
Диапазон-мотоцикл (<big><code>..</code></big>) проезжает от первого указанного объекта к его <code>.succ</code> (''succedent'' — «последующему»), и до последнего включительно. Три точки — то же, но мотоцикл остановился прямо перед последним элементом. Ещё раз:
=== Классы и объекты ===
Самодельные и неабстрактные, составные типы данных называются классами. Если для вас это новость, то почитайте [[Объектно-ориентированное программирование|викиучебник об объектно-ориентированном программировании]] или [[w:Объектно-ориентированное программирование|статью в Википедии]]. Вообще, в Руби ''всё'' в конечном счёте принадлежит классу <code>Object</code>.
<sourcesyntaxhighlight lang=ruby>str = "Aз есмь строка"
str.class #-> объект класса String
str.class.superclass #-> подкласса класса Object</sourcesyntaxhighlight>
Классы можно определять и создавать по ним объекты. Внутри класса может быть много всего интересного, и у него может быть фамильное [[w:Дерево (теория графов)|дерево]], то есть классы Руби поддерживают наследование. Однако заметим, что [[w:множественное наследование|множественное наследование]] в Руби не разрешается. И ещё много всего интересного можно сделать с классами и объектами. Но об этом позже.
 
 
При записи целых чисел сначала указывается знак числа (знак <code>+</code> обычно не пишется). Далее идет основание [[w:система счисления|системы счисления]], в которой задается число (если оно отлично от [[w:десятичная система счисления|десятичной]]): <code>0</code> — для [[w:восьмеричная система счисления|восьмеричной]], <code>0x</code> — для [[w:шестнадцатеричная система счисления|шестнадцатеричной]], <code>0b</code> — для [[w:двоичная система счисления|двоичной]]. Затем идет последовательность цифр, выражающих число в данной системе счисления. При записи чисел можно использовать символ подчеркивания, который игнорируется при обработке. Чтобы закрепить вышесказанное, посмотрим примеры целых чисел:
<sourcesyntaxhighlight lang=ruby># тип Fixnum
123_456 # подчеркивание игнорируется
-567 # отрицательное число
123_456_789_123_456 # подчеркивание игнорируется
-123_456_789_123_456 # отрицательное
07777777777777777777 # восьмеричное большое</sourcesyntaxhighlight>
 
Как видно из примеров, маленькие целые (<code>Fixnum</code>) и большие целые (<code>Bignum</code>) отличаются только значением.
Дробные числа задаются только в десятичной системе счисления, при этом для отделения дробной части используется символ <code>.</code> (точка). Для задания дробных чисел может быть применена и экспоненциальная форма записи: два различных представления <code>0.1234e2</code> и <code>1234e-2</code> задают одно и тоже число <code>12.34</code>.
 
<sourcesyntaxhighlight lang=ruby># тип Float
-12.34 # отрицательное дробное
0.1234е2 # экспоненциальная форма для числа 12.34
1234е-2 # экспоненциальная форма для числа 12.34</sourcesyntaxhighlight>
 
Следует упомянуть, что дробные числа имеют фиксированный диапазон значений в отличие от целых чисел. Этот недостаток легко устраняется подключением библиотеки <code>mathn</code> (подключаются рациональные и комплексные числа).
=== Арифметические операции ===
Арифметические операции в Ruby обычны: [[w:Сложение|сложение]] (<code>+</code>), [[w:Вычитание|вычитание]] (<code>-</code>), [[w:Умножение|умножение]] (<code>*</code>), [[w:Деление (математика)|деление]] (<code>/</code>), получение остатка от деления (<code>%</code>), возведение в степень (<code>**</code>).
<sourcesyntaxhighlight lang=ruby>6 + 4 #-> 10
6 - 4 #-> 2
6 * 4 #-> 24
6 / 4 #-> 1
6 % 4 #-> 2
6 ** 4 #-> 1296</sourcesyntaxhighlight>
 
Эти операции используются как дробными, так и целыми числами (а также рациональными дробями и комплексными).
 
Порядок вычисления обычный, как учили в школе. Для изменения приоритета применяются круглые скобки:
<sourcesyntaxhighlight lang=ruby>2 + 2 * 2 #-> 6
(2 + 2) * 2 #-> 8</sourcesyntaxhighlight>
 
Первое, что бросается в глаза — результат арифметической операции двух целых чисел всегда будет целым. Особенно это видно при делении:
<sourcesyntaxhighlight lang=ruby>1/3 #-> 0
2/3 #-> 0
3/3 #-> 1</sourcesyntaxhighlight>
 
{{Рамка}}Если все аргументы арифметического выражения целые числа, то результат будет целым, если хотя бы одно дробное, то результат будет дробным.
 
Посмотрим, каковы результаты, когда одно из чисел дробное.
<sourcesyntaxhighlight lang=ruby>6.0 + 4 #-> 10.0
6 - 4.0 #-> 2.0
6.0 * 4.0 #-> 24.0
6.0 / 4 #-> 1.5 (одно из чисел дробное, значит результат дробный)
6.0 % 4 #-> 2.0
6 ** 4.0 #-> 1296.0</sourcesyntaxhighlight>
 
Лучше проверить эти сведения самостоятельно. Для этого даже не обязательно [[#Начало работы|устанавливать интерпретатор Ruby]]. Достаточно лишь зайти на сайт [http://tryruby.hobix.com/ try ruby!].
 
Операции побитовой арифметики заимствованы из языка [[w:Си (язык программирования)|Си]]. На этот раз без всяких экзотических особенностей.
<sourcesyntaxhighlight lang=ruby>6 & 4 #-> 4
6 | 4 #-> 6
6 ^ 4 #-> 2
6 << 4 #-> 96
6 >> 4 #-> 0 (чересчур намного сдвинули)
~4 #-> -5 (операция только над одним аргументом)</sourcesyntaxhighlight>
 
Здесь, вроде, всё понятно и без дополнительных пояснений. А если не понятно, то справочник по языку [[w:Си (язык программирования)|Си]] поможет.
==== Операции с присвоением ====
Часто можно встретить выражения вида:
<sourcesyntaxhighlight lang=ruby>ogHo_4ucJIo += gpyroe_4ucJIo</sourcesyntaxhighlight>
 
Это выполнение операции сразу с присваиванием. Вышеуказанная запись равнозначна следующей:
<sourcesyntaxhighlight lang=ruby>ogHo_4ucJIo = ogHo_4ucJIo + gpyroe_4ucJIo</sourcesyntaxhighlight>
 
Вполне естественно, что вместо операции <code>+</code> может использоваться любая другая, а вместо чисел могут быть другие типы данных.
 
<sourcesyntaxhighlight lang=ruby>cTpoka = "едем"
cTpoka += ", "
cTpoka *= 3
maccuB = [1,2,3]
maccuB += [4,5]
maccuB #-> [1,2,3,4,5]</sourcesyntaxhighlight>
 
{{info|''При определении метода <code>+</code>, метод <code><nowiki>+=</nowiki></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> — массив). Посмотрим их действие на примере:<sourcesyntaxhighlight lang=ruby>7.to_f #-> 7.0
7.9.to_i #-> 7
7.to_s #-> "7"
"7".to_a #-> ["7"]</sourcesyntaxhighlight>
 
=== Случайное число ===
Часто требуется получить [[w:Случайная величина|случайное число]]. Пример:<sourcesyntaxhighlight lang=ruby>rand(100) # 86
rand #-> 0.599794231588021</sourcesyntaxhighlight>
 
В первом случае, метод <code>rand</code> возвращает <u>целое число</u> в диапазоне от 0 до 99 (на 1 меньше 100). Во втором случае, метод <code>rand</code> возвращает <u>дробное число</u> в диапазоне от 0.0 до 1.0 включительно. Различие в результате обусловлено передаваемым параметром:
* если параметр отсутствует, то генерируется <u>дробное</u> число в диапазоне от 0.0 до 1.0.
 
Есть способ предсказать весь ряд «случайных» чисел. Делается это при помощи метода <code>srand</code>. Ему передается целое число (идентификатор «случайной» последовательности). После этого, весь случайный ряд можно предугадать. Проведем эксперимент: берусь угадать массив, который будет сгенерен следующей программой.<sourcesyntaxhighlight lang=ruby>srand 123
Array.new(5){ rand(100) } #-> [69, 71, 28, 42, 22]</sourcesyntaxhighlight>
 
Если вы выполните данную программу у себя дома, то получите тот же самый массив. 123 — номер «случайной» последовательности. Измените его и массив изменится!
=== Хитрости ===
Задача: выдать целое число в [[w:двоичная система счисления|двоичной системе счисления]].
<sourcesyntaxhighlight lang=ruby>ucxogHoe_4ucJIo = 1234
puts sprintf("%b",ucxogHoe_4ucJIo) # метод sprintf заимствован из Си
puts ucxogHoe_4ucJIo.to_s(2) # современный метод - означает "по основанию",
# На самом деле, основание системы в моей версии ruby не может
# превышать 36, что вполне объяснимо - 10 цифр и 26 букв латинского
# алфавита.</sourcesyntaxhighlight>
 
Поменять порядок цифр данного числа на обратный:
<sourcesyntaxhighlight lang=ruby>ucxogHoe_4ucJIo = 1234
puts ucxogHoe_4ucJIo.to_s.reverse # метод reverse переворачивает строку</sourcesyntaxhighlight>
 
Получить значение N-го двоичного разряда данного целого числа:
<sourcesyntaxhighlight lang=ruby>ucxogHoe_4ucJIo, N = 1234, 5
puts ucxogHoe_4ucJIo[ N ]</sourcesyntaxhighlight>
 
Поменять целочисленные значения двух переменных без использования третьей переменной:
<sourcesyntaxhighlight lang=ruby>ucxogHoe_nepBoe, ucxogHoe_BTopoe = 134, 234
ucxogHoe_nepBoe, ucxogHoe_BTopoe = ucxogHoe_BTopoe, ucxogHoe_nepBoe
puts ucxogHoe_nepBoe, ucxogHoe_BTopoe</sourcesyntaxhighlight>
 
Округлить дробное число до двух разрядов:
<sourcesyntaxhighlight lang=ruby>gpo6Hoe_4ucJIo = 3.1415926535
puts ( gpo6Hoe_4ucJIo * 100 ).to_i.to_f / 100
puts (( gpo6Hoe_4ucJIo + 0.005) * 100 ).to_i / 100.0
puts sprintf( "%.2f", gpo6Hoe_4ucJIo ).to_f # полуСишный способ =)</sourcesyntaxhighlight>
На самом деле во второй строке оставляются 2 знака после запятой, а остальные просто отбрасываются безо всяких округлений, в то время как в третьей строке действительно происходит округление до двух знаков после запятой. Это легко проверить попытавшись округлить до 3 знаков после запятой:
<sourcesyntaxhighlight lang=ruby>gpo6Hoe_4ucJIo = 3.1415926535
puts ( gpo6Hoe_4ucJIo * 1000 ).to_i.to_f / 1000 # => 3.141
puts (( gpo6Hoe_4ucJIo + 0.0005) * 1000 ).to_i / 1000.0 # => 3.142
puts sprintf( "%.3f", gpo6Hoe_4ucJIo ).to_f # => 3.142</sourcesyntaxhighlight>
 
== Подробнее о массивах ==
=== Способы создания массива ===
Массив создается как минимум тремя способами. Первый способ:
<sourcesyntaxhighlight lang=ruby>[1,2,3,4,5,6]</sourcesyntaxhighlight>
 
Вы просто перечисляете элементы массива через запятую, а границы массива обозначаете квадратными скобками. С таким методом создания массива мы уже встречались. А теперь попробуем второй способ, через вызов метода <code>.new</code> класса Array. От слов перейдем к делу:
<sourcesyntaxhighlight lang=ruby>Array.new( 6 ){ |index| index + 1 } #-> [1,2,3,4,5,6]</sourcesyntaxhighlight>
 
Параметром метода <code>.new</code> является количество элементов будущего массива (в данном случае это число 6). В фигурных скобках указано, как мы будем заполнять массив. В данном случае, значение элемента массива будет больше на единицу его индекса. Третий способ заключается в создании объекта типа Range (диапазон) и вызове метода <code>.to_a</code>:
<sourcesyntaxhighlight lang=ruby>(1..6).to_a #-> [1,2,3,4,5,6]</sourcesyntaxhighlight>
 
Есть еще тысяча и один способ, но эти три используются чаще всего.
=== Диапазоны ===
Методом <code>to_a</code> очень удобно создавать из диапазона массив, содержащий упорядоченные элементы данного диапазона.
<sourcesyntaxhighlight lang=ruby>(1..10).to_a #-> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
('a'..'d').to_a #-> ['a', 'b', 'c', 'd']</sourcesyntaxhighlight>
 
Раз уж речь зашла о диапазонах, то давайте посмотрим, как они позволяют получать подмассивы. И насколько изящно у них это получается. Рассмотрим массив:
<sourcesyntaxhighlight lang=ruby>[ 'a', 'b', 'c', 'd', 'e']</sourcesyntaxhighlight>
 
Традиционно нумерация массива начинается с нуля и возрастает по одному:
Для Ruby '''двумерный массив''' — ''это не более чем массив, содержащий одномерные массивы''. Вот несколько примеров двумерных массивов:
 
<sourcesyntaxhighlight lang=ruby>[[1],[2,3],[4]] # разная длина элементов-массивов
[[1,2],[3,4]] # одинаковая длинна
[["прива","Привет"],["пока","Всего хорошего"]] # двумерный массив (классика)
[["прива","Привет"],[1,["пока","Всего хорошего"]]] # гибрид двух-трех-мерного массива</sourcesyntaxhighlight>
 
{{Внимание|
==== Транспонирование двумерного массива ====
Задача: дан двумерный массив. Вывести одномерный массив с максимумами каждого из столбцов. Хм… посмотрим сперва, как эта задача решается для строчек, а не столбцов:
<sourcesyntaxhighlight lang=ruby>maccuB_2D = [[1,2],[3,4]]
maccuB_2D.map{ |maccuB| maccuB.max } #-> [2,4]</sourcesyntaxhighlight>
 
Чтобы решить задачу в первоначальном варианте, нам надо лишь предварительно транспонировать массив (поменять местами строки и столбцы):
<sourcesyntaxhighlight lang=ruby>maccuB_2D = [[1,2],[3,4]]
maccuB_2D.transpose.map{ |maccuB| maccuB.max } #-> [3,4]</sourcesyntaxhighlight>
 
Метод <code>.transpose</code> как раз и занимается транспонированием. Это позволяет с легкостью решать задачи про столбцы приемами, схожими с задачами про строки.
 
<small>Замечание. Имена методов unshift/shift неинтуитивны. Во-первых, они не напоминают о том, что работа идет с головой массива, а не с хвостом, во-вторых ничего не говорят о том, идет заполнение или опустошение стека. Можно создать для этих методов псевдонимы с говорящими именами, например, feed/spit (кормить/выплевывать):<br>
<sourcesyntaxhighlight lang="ruby">
class Array
alias feed :unshift
alias spit :shift
end
</syntaxhighlight>
</source>
</small>
 
 
По японской традиции, имена логических методов принято заканчивать <code>?</code> (вопросительным знаком). Это позволяет также получить список логических методов, вызываемых в данном случае: просто отобрать из всех имеющихся методов те, что кончаются на <code>?</code>. Делается это при помощи небольшой вспомогательной программы:
<sourcesyntaxhighlight lang=ruby>maccuB = [1,2,2,3]
puts maccuB.methods.grep(/\?$/)</sourcesyntaxhighlight>
 
Для удобства, можно упорядочить полученный список:
<sourcesyntaxhighlight lang=ruby>maccuB = [1,2,2,3]
puts maccuB.methods.grep(/\?$/).sort</sourcesyntaxhighlight>
 
==== Есть элемент в массиве? ====
Как узнать, есть ли некоторый элемент в массиве? Попробуем решить эту задачу при помощи метода <code>.size</code> и итератора <code>.find_all</code>:
<sourcesyntaxhighlight lang=ruby>maccuB = [1,2,3,4,5,6,7]
uckomoe = 5 # число, которое мы будем искать
maccuB.find_all{ |elem| elem == uckomoe }.size != 0 #-> true
# это значит, что такое число есть</sourcesyntaxhighlight>
 
Использование связки из трех методов (<code>!=</code>, <code>.find_all</code> и <code>.size</code>) для такой задачи — возмутительно! Разработчики не могли с этим долго мириться и реализовали метод специально для этой задачи. Имя ему — <code>.include?</code>. Перепишем нашу задачу, но на этот раз будем использовать правильный метод:
 
Кстати, равенство нулю можно проверять и при помощи метода <code>.zero?</code>. А чётность тоже можно проверить разными способами:
<sourcesyntaxhighlight lang=ruby>(elem%2).zero?
(elem&1).zero?
(elem[0]).zero? # Этот вариант круче всех</sourcesyntaxhighlight>
 
Если на вопрос, заданный в блоке, ответ <code>true</code>, то <code>|elem|</code> (значение очередного элемента исходного массива), заносится в новый массив, который в итоге будет выводом из итератора <code>.find_all</code>.
=== Хитрости ===
Вот так можно сгенерировать «хороший пароль» — произвольную последовательность из чисел или латинских букв, общей длиной в 8 символов.
<sourcesyntaxhighlight lang=ruby>cumBoJIbI = ['a'..'z','A'..'Z','0'..'9'].map{ |range| range.to_a }.flatten
puts (0...8).map{ cumBoJIbI[ rand( cumBoJIbI.size ) ] }.join</sourcesyntaxhighlight>
Перемешать упорядоченный массив:
<sourcesyntaxhighlight lang=ruby>maccuB = [1,2,3,4,5,6,7]
maccuB.sort_by{ rand } #-> перемешанный массив</sourcesyntaxhighlight>
Выстроить элементы массива по убыванию без использования <code>.reverse</code>:
<sourcesyntaxhighlight lang=ruby>maccuB = [2,1,3,5,6,7,4]
maccuB.sort{ |x,y| y <=> x } #-> [7,6,5,4,3,2,1]</sourcesyntaxhighlight>
 
=== Задачи про массивы ===
==== Известен только тип значений ====
Сведения о типе значений использовать следует так: создать хеш, в котором будет определен элемент по умолчанию. Элементом по умолчанию должен быть нулевой элемент соответствующего типа, то есть для строки это будет пустая строка (<code>""</code>), для массива — пустой массив (<code>[]</code>), а для числа — нуль (<code>0</code> или <code>0.0</code>). Это делается, чтобы к пустому элементу можно было что-то добавить и при этом не получить ошибку.
<sourcesyntaxhighlight lang=ruby>xew = Hash.new( "" )
xew[ "песенка про зайцев" ] += "В темносинем лесу"
xew[ "песенка про зайцев" ] += ", где трепещут осины"
xew #-> { "песенка про зайцев"=>"В темносинем лесу, где трепещут осины" }</sourcesyntaxhighlight>
 
Или ещё пример:
<sourcesyntaxhighlight lang=ruby>xew = Hash.new( 0 )
xew[ "зарплата" ] += 60
xew[ "зарплата" ] *= 21
xew #-> {"зарплата"=>1260}</sourcesyntaxhighlight>
 
Но как известно из любого правила есть исключение: использовать нулевой элемент, когда значение будет записываться умножением, нежелательно. Потому как, даже не будучи npopokом <small style="color:#777">(Ых-хы-ыыы)</small>, можно предсказать результат. Он будет равен нулю.
==== Всё известно и дано ====
Если вам изначально известны все ключи и значения, то и записывайте их сразу в виде хеша:
<sourcesyntaxhighlight lang=ruby>{ "март" => 400, "январь" => 350, "февраль" => 200 }</sourcesyntaxhighlight>
{{Рамка}}Не изобретайте [[w:велосипед|велосипед]] и поступайте как можно проще.{{Акмар}}
 
Чтобы преобразовать ассоциативный массив в индексный, надо использовать метод <code>to_a</code>. Его используют все, кто не может запомнить методов работы с хешами.
 
<sourcesyntaxhighlight lang=ruby>xew = {"гаечный ключ" => 10, "разводной ключ" => 22}
xew.to_a #-> [ ["гаечный ключ", 10], [разводной ключ", 22 ] ]</sourcesyntaxhighlight>
 
Способ преобразования таков. Сперва пара и преобразуется в массив:
==== Хеш пустой? ====
Зададим вопрос «Хеш пустой?», но используя известный нам [[w:Лексикон|лексикон]]. Для начала спросим «Пустой хеш тебе не [[w:Брат|брат]]-[[w:Близнецы|близнец]]?»
<sourcesyntaxhighlight lang=ruby>nycTou_xew = {}
noJIHbIu_xew = { "гаечный" => 20, "замочный" => "английский", "разводной" => 34 }
 
nycTou_xew == {} #-> true
noJIHbIu_xew == {} #-> false</sourcesyntaxhighlight>
 
Можно спросить по другому — «Размер у тебя не нулевой?»
<sourcesyntaxhighlight lang=ruby>nycTou_xew = {}
noJIHbIu_xew = { "гаечный" => 20, "замочный" => "английский", "разводной" => 34 }
 
nycTou_xew.size.zero? #-> true
noJIHbIu_xew.size.zero? #-> false</sourcesyntaxhighlight>
Но давайте будем задавать правильные вопросы
<sourcesyntaxhighlight lang=ruby>nycTou_xew = {}
noJIHbIu_xew = { "гаечный" => 20, "замочный" => "английский", "разводной" => 34 }
 
nycTou_xew.empty? #-> true
noJIHbIu_xew.empty? #-> false</sourcesyntaxhighlight>
 
а то еще примут нас за приезжих…
Допустим, вы нашли максимальный элемент массива. И вам надо вывести результат на экран.
Вы можете поступить вот так:
<sourcesyntaxhighlight lang=ruby>maccuB = [4, 4, 2, 5, 2, 7]
puts maccuB.max #-> 7</sourcesyntaxhighlight>
 
Несмотря на правильность решения, вывод результата выглядит некрасиво.
Гораздо профессиональней будет написать вот такую программу:
 
<sourcesyntaxhighlight lang=ruby>maccuB = [4, 4, 2, 5, 2, 7]
puts "Максимальный элемент maccuB'a = #{maccuB.max}"
#-> Максимальный элемент maccuB'a = 7</sourcesyntaxhighlight>
 
Заметили, насколько привлекательней стал вывод результата, когда мы задействовали строки?
Деление в примере работает также как и метод <code>.split</code>, о котором речь пойдет чуть позже. А чтобы оно заработало у вас, необходимо добавить небольшой код в начало программы:
 
<sourcesyntaxhighlight lang=ruby>class String
alias / :split
end</sourcesyntaxhighlight>
 
Этот код как раз и говорит о том, что деление и <code>.split</code> — одно и тоже.
Правила в Ruby ограничиваются символами <code>/</code> (наклонная черта). Примеры правил:
 
<sourcesyntaxhighlight lang=ruby>/(жы|шы)/
/\w+@[\w\.]+\w+/i</sourcesyntaxhighlight>
 
Страшно? А мне нет. На самом деле работа с правилами очень проста. Главное привыкнуть и попрактиковаться.
Группировка используется, когда необходимо обрабатывать результат частями. Например, при обработке ссылок в [[w:HTML|HTML]]-документе удобно отдельно обрабатывать текст ссылки и [[w:URL|URL]]. Группировка также как и альтернатива, заключается в круглые скобки. Более того, альтернатива обрабатывается как группировка. Доступ к результату совпадения каждой группировки осуществляется посредством специальных переменных <code>$1, $2, ..., $9</code>. Подробнее группировки будут рассмотрены в подразделе «Правильная замена». Пример использования группировки:
 
<sourcesyntaxhighlight lang=ruby>"2+7*3".gsub(/(\d+)\*(\d+)/){ $1.to_i * $2.to_i } #-> "2+21"</sourcesyntaxhighlight>
 
Почти калькулятор!
Несколько лет назад (еще при жизни http://ruby-forum.ru) решали мы интересную задачу: как реализовать автоматический перенос на новую строку (wrap). Для тех, кто не застал те времена, уточню задание: дан текст, необходимо, вставить переносы таким образом, чтобы каждая из полученных строк была меньше n (для определенности <code>n=80</code>). Недавно я осознал, что не могу решить эту задачу тем способом, который был нами тогда найден. Я его просто не помнил… Решение нашлось быстро, достаточно было вспомнить, что на англ. языке эта задача именуется коротким и емким словом wrap.
 
<sourcesyntaxhighlight lang=ruby>class String
def wrap(col = 80)
gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/,"\\1\\3\n")
end
end</sourcesyntaxhighlight>
Немного о структуре кода… метод <code>.wrap</code> реализован для экземпляров класса <code>String</code>. Также стоит обратить внимание на то, что внутри правила (регулярного выражения) возможна «вставка» (как в «рабочих строках»). Используется сей метод следующим образом:
 
<sourcesyntaxhighlight lang=ruby>p "wrapping text with regular expressions".wrap( 10 )
# -> "wrapping\ntext with\nregular\nexpression\ns\n"</sourcesyntaxhighlight>
 
Теперь давайте разберемся с правилом. Чтобы не смущать неокрепшие умы, заменим вставку на 80. Правило станет короче и менее страшным.
 
<sourcesyntaxhighlight lang=ruby>(.{1,80})( +|$\n?)|(.{1,80})</sourcesyntaxhighlight>
Очевидно, что оно состоит из четырех частей:
Руби сам преобразует типы для некоторых простых операций. Например, при включении строки в другую он воспользуется имеющимся у объекта методом <code>.to_s</code>:
 
<sourcesyntaxhighlight lang=ruby>class Container
def to_s
"контейнер"
cont = Container.new
 
p "Это #{cont}" #-> "Это контейнер"</sourcesyntaxhighlight>
 
Если нужно, чтобы ваши объекты упорядочивались и сравнивались с обычными строками, следует применять примесь <code>Comparable</code> и единственный специальный метод <code>to_str</code>. Наличие этого метода у вашего объекта — знак для Ruby, что для сравнения следует применять не встроенный в <code>String</code> метод, а ваш.
 
<sourcesyntaxhighlight lang=ruby>class Container
include Comparable
def to_str
cont = Container.new
"контейнер" == cont #-> true</sourcesyntaxhighlight>
 
=== Задачи ===
Благодаря тому, что указание класса-носителя метода необязательно, на Ruby можно программировать в функциональном стиле, не заботясь о создании класса-«носителя» для каждой группы методов. Метод создается с помощью ключевых слов <code>def ... end</code>
 
<sourcesyntaxhighlight lang=ruby>def sum(a, b)
return a + b
end
sum(10, 2) #-> 12</sourcesyntaxhighlight>
 
Ruby по умолчанию возвратит из метода результат последнего выполненного выражения, поэтому в конце метода или в условных конструкциях слово return можно опускать. Поскольку методы могут быть переопределены в процессе выполнения программы, можно «на ходу» переписать метод так:
 
<sourcesyntaxhighlight lang=ruby>def sum(a, b)
a + b
end
sum(10, 2) #=> 12</sourcesyntaxhighlight>
 
<!-- Про case и if планируется рассказывать в самом конце, а программу is_odd можно переписать как !(number%2).zero?
Условное выражение и выражение <code>case</code> также возвращают свой результат, поэтому если метод проверяет условия, удобно написать его примерно так:
 
<sourcesyntaxhighlight lang=ruby>
def is_odd(number)
case number % 2
end
is_odd(4) #-> false
is_odd(3) #-> true</sourcesyntaxhighlight>
 
И return, и промежуточное присвоение можно опустить. Подобные свойства Ruby позволяют делать программы невероятно сжатыми без существенной потери читаемости.
У методов могут быть необязательные аргументы; для этого им нужно присвоить значение, которое следует применять «по умолчанию»
 
<sourcesyntaxhighlight lang=ruby>def sum(a, b = 5)
a + b
end
sum(10, 2) #-> 12
sum(10) #-> 15</sourcesyntaxhighlight>
 
==== Методы с восклицательным и вопросительным знаком ====
Обычно программист, чтобы проверить, пуст ли массив, посмотрит его длину:
 
<sourcesyntaxhighlight lang=ruby>arr = []
if arr.length == 0
puts "empty"
else
puts "not empty"
end</sourcesyntaxhighlight>
 
У массива в Ruby есть метод-предикат <code>.empty?</code>, возвращающий <code>true</code> если массив пуст, и метод-предикат <code>.any?</code>, возвращающий <code>true</code> если массив содержит хотя бы один элемент.
 
<sourcesyntaxhighlight lang=ruby>arr = []
if arr.empty?
puts "empty"
else
puts "not empty"
end</sourcesyntaxhighlight>
 
Если вы реализуете программу, которой будут пользоваться другие, считается хорошим тоном реализовывать методы-предикаты.
Еще одна их прелесть — сочетание с модификаторами выражения:
 
<sourcesyntaxhighlight lang=ruby>arr = [1,2 ,3]
p "Array has something" if arr.any?</sourcesyntaxhighlight>
 
Методы с восклицательным знаком на конце меняют объект, к которому привязаны.
 
<sourcesyntaxhighlight lang=ruby>string = " Some string with spaces "
string.strip! #-> "Some string with spaces" - возвращает результат операции...
 
string #-> "Some string with spaces" #=> ...и меняет состояние объекта-адресата</sourcesyntaxhighlight>
 
==== Методы присвоения ====
Знак равенства в конце названия метода означает, что этот метод присваивает свойству объекта значение:
 
<sourcesyntaxhighlight lang=ruby>class Bottle
def capacity
@capacity
 
bytilka = Bottle.new
bytilka.capacity = 10 #-> 10, автоматически преобразуется в вызов метода capacity=</sourcesyntaxhighlight>
 
==== Операторы ====
Операторы (умножение, деление, возведение в степень и так далее — вплоть до сравнения!) — тоже методы. Например:
 
<sourcesyntaxhighlight lang=ruby>class BeHuK
def+(another)
12 + another
 
meTeJIka = BeHuK.new
meTeJIka + 10 #-> 22</sourcesyntaxhighlight>
 
Это применяется, например, во встроенном в Ruby объекте <code>Time</code>. При прибавлении к нему целого числа он возвращает новый объект <code>Time</code> с добавленным количеством секунд:
 
<sourcesyntaxhighlight lang=ruby>t = Time.now #-> Sun Jun 11 20:29:51
t + 60 #-> Sun Jun 11 20:30:51 - на минуту позже</sourcesyntaxhighlight>
 
То же самое характерно для имеющегося в стандартной библиотеке класса <code>Date</code>, но, в отличие от <code>Time</code>, он считает дни вместо секунд
 
<sourcesyntaxhighlight lang=ruby>require 'date'
d = Date.today #-> Sun Jun 11
d + 1 #-> Mon Jun 12 - на день позже</sourcesyntaxhighlight>
 
==== «Поглощение» аргументов метода ====
Можно «свернуть» аргументы с помощью звездочки — тогда метод получит массив в качестве аргумента
 
<sourcesyntaxhighlight lang=ruby>def sum(*members)
members[0] + members[1]
end
sum(10, 2) #-> 12</sourcesyntaxhighlight>
 
Поскольку теперь наш метод принимает неограниченное количество элементов, мы можем пользоваться ими как массивом и в теле функции
 
<sourcesyntaxhighlight lang=ruby>def sum(*members)
initial = 0
members.collect{ | item | initial += item }
end
sum(10, 2) #-> 12
sum(10, 2, 12, 34) #-> 58</sourcesyntaxhighlight>
 
Можно разделить аргументы на обязательные и необязательные, просто пометив последний аргумент «звездочкой». Если методу будут переданы только обязательные аргументы, в переменной «со звездочкой» в теле метода будет пустой массив.
Звездочкой полезно пользоваться и когда нужно передать методу аргументы, но не хочется указывать их по отдельности. Следуя тому же примеру:
 
<sourcesyntaxhighlight lang=ruby>array_to_sum = [10, 2, 12, 34]
sum(*array_to_sum) #-> 58</sourcesyntaxhighlight>
 
=== Подробнее о блоках ===
Поскольку при отсутствии аргументов скобки необязательны, простейшая запись такова:
 
<sourcesyntaxhighlight lang=ruby>puts (1..3).map{ "Bay!" } # выводит Bay! три раза</sourcesyntaxhighlight>
 
Важно помнить, что блок использует методы и переменные, указанные при его создании, то есть блок ''захватывает контекст'', но переменные определенные в блоке остаются для него локальными!
 
<sourcesyntaxhighlight lang=ruby>puts (1..3).map{ word = 'Bay!'; word } #-> выводит Bay! три раза
# поскольку блок знает про переменную word и она определена в нем
puts word # вызывает сообщение об ошибке - вне блока об этой переменной ничего не известно</sourcesyntaxhighlight>
 
Как уже упоминалось, если блок многострочный целесообразней пользоваться формой с <code>do ... end</code>
Другое замечательное свойство блоков — они, как и функции, могут принимать аргументы. В таком случае метод, которому передан блок, сам «решает», что этот блок получит в качестве аргумента. Например, уже продемонстрированный метод <code>.map</code> еще и передает блоку аргумент, который можно захватить следующим образом:
 
<sourcesyntaxhighlight lang=ruby>puts (1..3).map do | index |
index
end</sourcesyntaxhighlight>
 
В данном случае при каждом выполнении блока переменная <code>index</code> будет устанавливаться на положение итератора, начиная с 1!
Аргументы метода указываются после открывающей фигурной скобки или после слова <code>do</code> через запятую, и разграничиваются двумя вертикальми чертами. Чтобы не перепутать черту со строчной латинской L, принято «отбивать» аргументы блока от вертикальной черты пробелами.
 
<sourcesyntaxhighlight lang=ruby>method { | argument | .. }</sourcesyntaxhighlight>
 
==== Свои методы с блоками ====
Таким образом, гораздо лучше вместо введения обязательного параметра задавать блок по-умолчанию:
 
<sourcesyntaxhighlight lang=ruby>def func(a,&block)
return a if a
block ||= lambda{ | cJloBa | puts "!!! " + cJloBa }
 
func(false) { | cJloBa | puts "??? " + cJloBa } #-> ??? u pa3
#-> ??? u gBa</sourcesyntaxhighlight>
 
Здесь lambda — пустая функция, а block.call — явный способ вызова блока кода на выполнение.
Типичное применение блока — когда после выполнений некой операции нужно «вынести мусор»: закрыть открытый ресурс или отсоединиться от сети. Предположим, что мы пишем метод для интернет-системы. При этом мы хотим выполнить несколько операций, но чтобы их выполнить, нужно подключить пользователя к Сети. После того, как операции завершились, надо его так же незаметно отключить.
 
<sourcesyntaxhighlight lang=ruby>connected { download_email }</sourcesyntaxhighlight>
 
В данном случае мы пишем только блок с <code>download_email</code>, все заботы по открытию (а главное — закрытию) соединения возьмет на себя метод <code>connected</code>:
 
<sourcesyntaxhighlight lang=ruby>def connected
connect_to_internet
result = yield
disconnect
result
end</sourcesyntaxhighlight>
 
В данном случае мы сохраняем то, что вернул блок в метод, закрываем соединение и возвращаем результат блока как свой собственный.
Если воспользоваться встроенной проверкой исключений, то метод принимает такой вид:
 
<sourcesyntaxhighlight lang=ruby>def connected
connect_to_internet
begin
end
result
end</sourcesyntaxhighlight>
 
Тогда даже если метод вызовет ошибку, соединение все равно будет закрыто.
==== Ширяевский .size ====
Студент МЭТТ Ширяев Денис, на одном из зачетов предложил использовать метод <code>.size</code> в качестве итератора. Он использовал его для подсчета количества элементов массива, удовлетворяющих условию. По сути, он предложил укоротить связку <code>.find_all{ ... }.size</code>. Вот как будет выглядеть программа подсчета количества четных элементов массива:
<sourcesyntaxhighlight lang=ruby>maccuB = [1,2,3,4,5,6]
maccuB.size{ |i| (i%2).zero? } #-> 3</sourcesyntaxhighlight>
 
Чтобы заставить работать данную программу, необходимо перед использованием итератора <code>.size</code> написать следующий код, который будет реализовывать эту функциональность:
<sourcesyntaxhighlight lang=ruby>class Array
def size( &block )
block ? inject( 0 ){ |count,elem| (yield elem ) ? count + 1 : count } : length
end
end</sourcesyntaxhighlight>
 
Метод реализован только для массивов, но возможно его добавление к хешам или строкам.
==== Случайное число из диапазона ====
Студенты часто возмущаются, почему чтобы получить случайное число от 3 до 6 нужно писать нечто невнятное вида:
<sourcesyntaxhighlight lang=ruby>3 + rand( 4 )</sourcesyntaxhighlight>
 
Откуда чего берется? Почему нельзя написать проще? Например вот так:
<sourcesyntaxhighlight lang=ruby>(3..6).rand</sourcesyntaxhighlight>
 
Действительно, почему? Давайте добавим такую функциональность к классу Range:
<sourcesyntaxhighlight lang=ruby>class Range
def rand
first + Kernel.rand( last - first + ( exclude_end? ? 0 : 1 ) )
end
end</sourcesyntaxhighlight>
 
Для проверки можно выполнить следующий код:
<sourcesyntaxhighlight lang=ruby>p Array.new(100){ (3..6).rand }.uniq.sort #-> [3,4,5,6]</sourcesyntaxhighlight>
 
Что и требовалось реализовать! Кстати, данная реализация имеет один изъян: для строковых диапазонов, метод <code>Range#rand</code> будет выдавать ошибку. Решается проблема достаточно просто. Надо реализовать <code>Array#rand</code> (получение случайного элемента массива), а внутри <code>Range#rand</code> вызывать связку <code>.to_a.rand</code>. Теперь тоже самое, но на Ruby:
<sourcesyntaxhighlight lang=ruby>class Array
def rand
self[ Kernel.rand( size ) ]
to_a.rand
end
end</sourcesyntaxhighlight>
 
Для проверки выполним следующий код:
<sourcesyntaxhighlight lang=ruby>p Array.new(100){ ('a'..'c').rand }.uniq.sort #-> ["a","b","c"]</sourcesyntaxhighlight>
 
Странно, но видимо все работает!
Уничтожение класса несколько сложнее, но тоже возможно
 
<sourcesyntaxhighlight lang=ruby>Object.send(:remove_const, :BeHuK)</sourcesyntaxhighlight>
 
После этого BeHuK будет существовать только для объекта-экземпляра
 
<sourcesyntaxhighlight lang=ruby>class BeHuK
end
 
BeHuK # => Ошибка NameError: неизвестная константа BeHuK
 
meTeJIka.class #=> BeHuK, все еще существует для экземпляра</sourcesyntaxhighlight>
 
Это свойство Ruby крайне полезно, если нужно создать класс, наследующий от другого, но при этом имеющий другого родителя. Например:
 
<sourcesyntaxhighlight lang=ruby># в чужой программе...
class Connection < Socket
# ... много-много методов connection...#
end
 
# в итоге чужая программа будет использовать созданный нами Connection!</sourcesyntaxhighlight>
 
{{Внимание|Полная замена чужих классов довольно опасна, но бывают ситуации, когда эта методика спасает.}}
== Матрицы и вектора ==
Давно хотел написать историю про то, как использовать [[w:Матрица (математика)|матрицы]] и [[w:Вектор (математика)|вектора]] в Ruby, но руки дошли только сейчас. Мое знакомство с реализацией матричного исчисления в Ruby началось еще в мою бытность студентом [http://www.msiu.ru МГИУ]. И надо сказать, что это знакомство было неудачным. Дело в том, что простейшая программа вычисления определителя матрицы
<sourcesyntaxhighlight lang=ruby>require 'matrix'
p Matrix[[1,-2,3],[3,4,-5],[2,4,1]].det #-> -50</sourcesyntaxhighlight>
давала неверный результат (правильный ответ = 62). Как выяснилось позднее, эта проблема связана со спецификой целочисленной арифметики в Ruby (<одна вторая в Руби — нуль>). Предположив это, я решил, что проблема легко решится, если я преобразую элементы матрицы к дробному типу (классу Float):
<sourcesyntaxhighlight 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</sourcesyntaxhighlight>
И в некоторых случаях это меня действительно выручало. Но не всегда… стоило появиться делению на 3, как появлялись ненужные остатки и погрешности. И чем больше исходная матрица, тем больше вероятность появления таких остатков. В некоторых случаях это было несущественным, а в остальных приходилось прибегать к услугам специальных математических пакетов (например,[[w:Maxima|Maxima]]). Было жутко неудобно (я тогда писал курсовой, который решал все варианты контрольной работы по предмету <Математическое программирование>. Да простит меня преподаватель, который так и не понял секрета тотальной успеваемости группы) и обидно за такую «кривую реализацию».
 
=== Как создать новую матрицу? ===
Самый простейший способ создать матрицу — использовать метод <батарейка> (метод [] выглядит как индикатор заряда батареи на сотовом телефоне):
<sourcesyntaxhighlight lang=ruby>require 'mathn'
Matrix[[1, -2, 3], [3, 4, -5], [2, 4, 1]]</sourcesyntaxhighlight>
 
Этот способ создания мы уже с вами видели раньше. Обратите внимание, что для использования матриц необходимо подключать библиотеку <tt>mathn</tt>.
=== Как послать в матрицу 2-мерный массив? ===
Немножко перефразируем вопрос: <как преобразовать двумерный массив в матрицу>? Пусть ответ будет неожиданным, но это делается при помощи того-же метода <батарейка>:
<sourcesyntaxhighlight lang=ruby>require 'mathn'
maccuB = [[1, -2, 3], [3, 4, -5], [2, 4, 1]]
Matrix[ *maccuB ]</sourcesyntaxhighlight>
 
Оператор * преобразует <tt>maccuB</tt> в список параметров, что позволяет существенно упросить процесс создания матрицы. Более того, это единственно удобный метод ее создания. Все остальные методы создания матрицы (например, метод <tt>.new</tt>) работают не столь изящно и интуитивно.
=== Как изменить элемент матрицы? ===
И вот тут всплывает <недоделанность> матриц. Метода для изменения элемента матрицы в них нет! Для того, чтобы изменить элемент матрицы, надо преобразовать матрицу в массив, изменить элемент массива и преобразовать массив в матрицу. Примерно вот так (меняем элемент с индексом <tt>0,0</tt>):
<sourcesyntaxhighlight lang=ruby>require 'mathn'
maTpuLLa = Matrix[[1,-2,3],[3,4,-5],[2,4,1]]
maccuB = maTpuLLa.to_a
maccuB[0][0] = 0
p Matrix[ *maccuB ] #-> Matrix[[0, -2, 3], [3, 4, -5], [2, 4, 1]]</sourcesyntaxhighlight>
 
==== Исправляем оплошность разработчиков ====
Для начала, рассматриваем поближе библиотеку <tt>matrix</tt> (исходник или описание в <tt>fxri</tt>) и выясняем, что для получения значения элемента используется метод <батарейка> с двумя аргументами. Вполне закономерно использовать метод <батарейка равно> (то есть []=) для изменения элемента. Сейчас мы его и реализуем:
<sourcesyntaxhighlight lang=ruby>require 'mathn'
class Matrix
def []=( i, j, value )
maTpuLLa = Matrix[[1,-2,3],[3,4,-5],[2,4,1]]
maTpuLLa[0,0] = 5
p maTpuLLa #-> Matrix[[5, -2, 3], [3, 4, -5], [2, 4, 1]]</sourcesyntaxhighlight>
 
Ну вот как-то примерно так… Почему не могли этого сделать разработчики, я так и не понял. Скорее всего по идеологическим соображениям (<не дело, чтобы матрицы вели себя как простые массивы>).
=== Зачем нужны вектора? ===
Во-первых, для Ruby вектор — это объект класса <tt>Vector</tt>. Подключается он одновременно с матрицами (класс <tt>Matrix</tt>). Во-вторых, очень похож на массив, но с одним существенным отличием (cобственно это отличие и определяет полезность вектора): массивы и вектора по разному складываются и вычитаются. Давайте рассмотрим небольшой пример:
<sourcesyntaxhighlight lang=ruby>require 'mathn'
maccuB = [1,2,3,4,5]
BekTop = Vector[ *maccuB ]
p BekTop + BekTop #-> Vector[2, 4, 6, 8, 10]
p maccuB + maccuB #-> [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]</sourcesyntaxhighlight>
 
{{info|Обратите внимание, что <tt>BekTop</tt> создается точно также, как и матрица. По сути, вектор - это матрица, которая состоит лишь из одной строки. А матрица, в свою очередь - это массив векторов}}
 
Но суть не в этом. После того, как я ее нашел, у меня появилась идея-фикс: реализовать эту программу заново, но с высоты текущего уровня. Вот только для этого надо вспомнить хотя бы что-то по поводу симплексного алгоритма (чтение моего старого программного кода мне помогло мало). Первое, что пришло мне на ум — там был метод Гаусса (правда, насколько я помню, не в чистом виде). Вот как раз его мы сейчас и реализуем.
<sourcesyntaxhighlight lang=ruby>require 'mathn'
ypaBHeHue = [Vector[1,2,1,1],Vector[1,5,6,2],Vector[1,5,7,10]]</sourcesyntaxhighlight>
 
Почему был выбран именно массив векторов, а не матрица или двумерный массив? Дело в том, что в методе Гаусса приходится выполнять такие векторные операции, как: вычитание векторов и деление вектора на [[w:Скаляр|скаляр]]. Поэтому смысла создавать матрицу (векторных операций не предусмотрено) или двумерный массив (придется реализовывать эти операции) — нет! Кстати, вполне возможно создать массив векторов и из двумерного массива (что мы и сделаем в следующем примере). Итак, приступим к реализации прямого хода метода Гаусса:
<sourcesyntaxhighlight lang=ruby>require 'mathn'
ypaBHeHue = [[1,2,1,1],[1,5,6,2],[1,5,7,10]].map{ |array| Vector[ *array ] }
# Прямой ход метода Гаусса
ypaBHeHue[0] /= ypaBHeHue[0][0]</sourcesyntaxhighlight>
 
И вот тут нас ждет неприятный сюрприз: у векторов не реализован метод /. Смотрим в файл <tt>matrix.rb</tt> (который мирно лежит в директории со стандартными библиотеками). Действительно нет! Метод * (умножить) есть, а разделить — нет. Ну и ладно, мы — программисты не гордые… сами напишем!
<sourcesyntaxhighlight lang=ruby>require 'mathn'
 
class Vector
ypaBHeHue = [[1,2,1,1],[1,5,6,2],[1,5,7,10]].map{ |array| Vector[ *array ] }
# Прямой ход метода Гаусса
ypaBHeHue[0] /= ypaBHeHue[0][0]</sourcesyntaxhighlight>
 
Во! Теперь работает (только со скалярами, но нам больше и не надо). Заканчиваем реализовывать прямой проход метода Гаусса:
<sourcesyntaxhighlight lang=ruby>require 'mathn'
 
class Vector
ypaBHeHue[2] /= ypaBHeHue[2][2]
 
p ypaBHeHue #-> [Vector[1,2,1,1], Vector[0,1,5/3,1/3], Vector[0,0,1,8]]</sourcesyntaxhighlight>
 
Судя по всему, программа работает. Кстати, обратите внимание, что результирующие вектора содержат рациональные дроби). Теперь добавим обратный ход метода Гаусса:
<sourcesyntaxhighlight lang=ruby>require 'mathn'
 
class Vector
ypaBHeHue[0] -= ypaBHeHue[1]*ypaBHeHue[0][1]
 
p ypaBHeHue #-> [Vector[1,0,0,19], Vector[0,1,0,-13], Vector[0,0,1,8]]</sourcesyntaxhighlight>
 
Ну вот и все… вроде как решение получили. Но было бы замечательно, если бы выводилось не все уравнение, а только столбец свободных членов. Задачка простенькая, но важная. Давайте ее решим:
<sourcesyntaxhighlight lang=ruby>p ypaBHeHue.map{ |vector| vector.to_a }.transpose[-1] #-> [19,-13,8]</sourcesyntaxhighlight>
 
Теперь задачу можно считать решенной! Жаль только, что программа работает только для уравнения 3х4. Надо бы добавить несколько итераторов, чтобы они самостоятельно определяли размерность уравнения. Для этого нужно проследить чередование индексов и записать для них итераторы (хоть я и недолюбливаю <tt>.each</tt>, но для данного случая он пришелся как нельзя кстати). Расписывать преобразование я не буду, так как не вижу в этом большой надобности. Поэтому, смотрите сразу то, что у меня получилось:
<sourcesyntaxhighlight lang=ruby>require 'mathn'
 
class Vector
}
 
p ypaBHeHue.map{ |vector| vector[-1] } #-> [19,-13,8]</sourcesyntaxhighlight>
 
Обратите внимание, что <tt>ypaBHeHue</tt> задается через матрицу (которая, не без помощи метода <tt>.row_vectors</tt>, преобразуется в массив векторов). Также обратите внимание, что получить последний столбец можно посредством итератора <tt>.map</tt> и метода «батарейка».
 
Файл с ERB-шаблоном (<tt>index.html</tt>) будет выглядеть следующим образом:
<sourcesyntaxhighlight lang=rails><html>
<body>
<center>
</center>
</body>
</html></sourcesyntaxhighlight>
 
Переменную шаблон мы убираем, а вместо нее вставим считывание файла c ERB-шаблоном (<tt>index.html</tt>).
=== Как пропинговать компьютер в сети? ===
Открываем новую серию статей, которые будут рассказывать про использование встроенных библиотек Ruby. Первая статья будет посвящена написанию утилиты <tt>ping</tt> (в очень упрощенной форме). Смотрим в стантартную библиотеку и обнаруживаем файлик <tt>ping.rb</tt>. Смотрим в него и обнаруживаем метод <code>pingecho</code>. Метод используется следующим образом:
<sourcesyntaxhighlight lang=ruby>require 'ping'
host = ARGV[0] || "localhost"
printf("%s alive? - %sn", host, Ping::pingecho(host, 5))</sourcesyntaxhighlight>
Данный метод имеет один маленький недостаток. Он не отслеживает никаких ошибок кроме <code>Timeout::Error и Errno::ECONNREFUSED</code>. Меня это немного смутило и поэтому я убрал явное указание на Timeout. Получился примерно такой метод:
<sourcesyntaxhighlight lang=ruby>require 'socket'
require 'timeout'
def ping( host, service = "echo",timeout = 5)
end
end
p ping(ARGV[0] || "localhost")</sourcesyntaxhighlight>
Итак, давайте разберем, что делает наш метод. Он создает соединение посредством класса TCPSocket и тут же закрывает его. Если соединение проходит слишком долго (хост не существует в сети) или произошла какая-то другая ошибка (не поддерживается протокол или еще что-то), то метод возвращает <code>false</code>. Если удаленный хост явно отверг наш запрос или принял его, то мы возвращаем <code>true</code>. Как видите, ничего сложного в этом нет.
 
 
Ее я решил писать в логи следующим образом… Каждый день будет создаваться файл лога и каждый час в него будет писаться информация о работе службы. В качестве планировщика заданий использовался виндовый [http://www.nncron.ru/ Cron], который запускал мою программу в нужное время. Для начала я написал программу, которая соединяется со службой SMTP и получает от него баннер:
<sourcesyntaxhighlight lang=ruby>require 'socket'
 
request = ""
File.open( Time.now.strftime('d:/logs/smtp/%Y_%m_%d.smtp'), File::APPEND | File::CREAT | File::WRONLY ){ |f|
f.puts( "#{sprintf( '%02d',Time.now.hour )} | #{end_time-begin_time} | #{request}" )
}</sourcesyntaxhighlight>
Как и следовало ожидать, программа не работала. Она подвисала и не хотела ничего писать в файл. Висела она на строчке:
<sourcesyntaxhighlight lang=ruby>request = t.gets.chomp</sourcesyntaxhighlight>
Чтобы разобраться с проблемой, пришлось читать книжку. Слава богу, что под рукой оказалась книга [http://books.dore.ru/bs/f1bid1824.html TCP/IP. Учебный курс]. В ней на с.345 черным по серому была начертана схема взаимодействия SMTP протокола. Как оказалось, чтобы получить баннер от службы, надо послать команду <tt>NOOP</tt>. Переписываем наш фрагмент программы.
<code>require 'socket'
</tt>
Нам нужен только <tt>real</tt>. Все остальное — для более детального анализа. Поэтому немного доработаем результат блока <tt>measure</tt>:
<sourcesyntaxhighlight lang=ruby>beachmark = Benchmark.measure{
timeout(1){
t = TCPSocket.open('mail.scli.ru', 'smtp')
t.close
}
}.real</sourcesyntaxhighlight>
Или можно просто вместо <tt>Benchmark.measure</tt> использовать <tt>Benchmark.realtime</tt>. Теперь надо бы разделить ошибки по таймауту и ошибки соединения. Для этого надо добавить лишь еще один блок <code>;rescue</code>. Помимо этого мне не понадобится все сообщение от службы. Мне и кода сообщения достаточно. Итак, смотрим, что получилось:
<code>require 'socket'
 
Каждый раз при запуске программы, мы скачиваем заглавную страницу сайта и выдираем оттуда даты и заголовки новостей. Потом читаем файл с подобным же набором даных. Читаем весь файл, хотя можно читать только последние n-строк. Но мы будем создавать каждый месяц новый файл и поэтому особой загрузки памяти происходить не должно. Далее сравниваем эти два набора данных и с помощью пересечения и вычитания множеств мы получаем те данные, которых до сих пор нет в файле. Как раз эти данные мы и добавляем в конец файла. ВСЕ! Просто и лаконично. Теперь код, который выполняет поставленную задачу:
<sourcesyntaxhighlight lang="ruby">require 'net/http'
h = Net::HTTP.new( 'www.minjust.ru', 80 )
from_file = f.readlines.map{ |str| str.chomp }.compact
f.puts( ( from_inet - (from_file & from_inet) ).sort )
}</sourcesyntaxhighlight>
В результате мы получаем файлы, которые могут быть с легкостью использованы для составления отчетов. Будут предложения по оптимизации — пишите на форум.
 
Но вернемся к заказанной мне программе. Она будет состоять из двух частей: обычная <tt>html</tt>-страница (<tt>index.html</tt>) и [[w:Сервлет|сервлет]] (<tt>/input</tt>). Страница <tt>public_html/index.html</tt> будет содержать форму в которую будут вводится исходные данные. Ее задачей будет формирование запроса для сервлета. Сервлет <tt>/input</tt> будет получать номера подразделений, а выдавать их названия и ФИО действующих начальников. Вот код <tt>public_html/index.html</tt>:
 
<sourcesyntaxhighlight lang="html4strict"><nowiki><HTML><BODY><form </nowiki><u>action='/input' method='post'</u><nowiki>><div align=center>
<table border=0 bgcolor=#000000 width=350><tr><td><table border=0 bgcolor=#CCCCFF width=100%>
<tr><td align=right>Лит-1 - 01</td><td><INPUT </nowiki><u>TYPE="checkbox" NAME="check_01"</u><nowiki>></td>
<tr><td align=right>Поленов</td><td><INPUT TYPE="checkbox" NAME="check_011"></td>
<td><INPUT TYPE="checkbox" NAME="check_012"></td><td>Копылов</td></tr>
</table></td></tr></table></div></form></BODY></HTML></nowiki></sourcesyntaxhighlight>
 
{{info|Geka, когда увидел этот код, воскликнул: "И не лень было такой код писать?" На что я ему резонно ответил, что этот код является результатом программы-генератора, которую я не привожу здесь, чтобы не отвлекать внимание читателя от более интересной темы}}
Теперь рассмотрим серверную часть программы (сервлет <tt>/input</tt>), которая будет обрабатывать запросы (сформированные файлом <tt>public/index.html</tt>). Сервлет является частью сервера. Поэтому листинг сервера будет одновременно и листингом сервлета.
 
<sourcesyntaxhighlight lang="ruby"><u>bosses = </u>{
'check_01' => "\tНачальнику Лит-1\tБокову Ю.В.",
'check_02' => "\tНачальнику ГЛЦЧ\tНазарову А.В.",
</textarea><br /><input type='submit' value='Повторим?'></form></div></body></html></nowiki>!
}
server.start</sourcesyntaxhighlight>
 
Начинаем разбираться с кодом веб-сервера:
::: для операционных систем семейства Windows
: Кстати, я столкнулся с тем, что под Windows CGI-сервлеты отказывались работать. Это связано с тем, что <tt>WEBrick</tt> считает, что для того, чтобы запустить скрипт достаточно просто его вызывать (<tt>./sctipt_name</tt>) и он сам запустится. Понятное дело, что в Windows такое работать не будет. Поэтому мне пришлось немножко переписать часть библиотеки WEBrick, которая отвечает за запуск CGI-программ. Для того, чтобы мои изменение стали доступны и вам, я написал небольшую программку, которая устанавливает себя куда надо:
<sourcesyntaxhighlight lang=ruby>File.open(Config::CONFIG['rubylibdir']+'/webrick/httpservlet/cgi_runner.rb','w'){|file|
file.puts IO.read($0).split('#'*10).last
}
else
exec(ENV["SCRIPT_FILENAME"])
end</sourcesyntaxhighlight>
: Немного поясню. Первая строчка — это программа, которая записывает нижеследующий код в папку с библиотеками.
: Функциональность CGI-сервлета описана в классе WEBrick::HTTPServlet::CGIHandler
Для того, чтобы было проще пользоваться всеми видами сервлетов WEBrick я написал небольшую программку, которая создает метод mount_file (для файлового сервлета), mount_erb (для ERB-сервлета) и mount_cgi (для CGI-сервлета). Как вы могли заметить mount_proc уже существует (собственно, его название и послужило прототипом для остальных). Вот эта программа:
 
<sourcesyntaxhighlight lang=ruby>require 'webrick'
module WEBrick
class HTTPServer
}
end
end</sourcesyntaxhighlight>
 
Теперь можно использовать эти сервлеты следующим образом:
 
<sourcesyntaxhighlight lang=ruby>require 'webrick'
module WEBrick
class HTTPServer
server.mount_cgi('/hello','cgi-bin/hello.exe' )
server.mount_cgi('/hello.cgi','cgi-bin/hello.rb' )
server.start</sourcesyntaxhighlight>
 
Ну вот вроде и все, что можно сказать по сервлетам.
583

правки