- один из самых молодых языков программирования. Своим именем он обязан драгоценному камню рубину (по аналогии с другим широко распространенным языком программирования Perl - жемчуг). Вот как описывает его создатель, японский программист Юкихиро Мацумото (Yukihiro Matsumoto): "Это мощный и динамический объектно-ориентированный язык с открытыми исходниками, который я начал разрабатывать в 1993 году.
В Японии сильно потеснил такие известные языки как и Perl (а книга " (к сожалению, пока не имеющие русского перевода). У этого языка очень неплохие шансы стать действительно популярным - ведь он вобрал в себя достоинства других языков, учтя их недостатки.
входит в стандартную поставку ОС GNU\Linux (начиная с версии 7.2), а пользователям MS Windows для первого знакомства стоит порекомендовать его несколько устаревшую версию (1,2 Мбайт), легко умещающуюся на одной дискете, которая включает, помимо интерпретатора языка и библиотек, руководство пользователя, является свободно распространяемым продуктом, поэтому вы можете не беспокоиться ни о его стоимости, ни об ограничениях в его использовании.
Этот язык несомненно является одним из лучших в качестве первого языка программирования, изучаемого студентами и школьниками. Быстрый в других операционных системах практически ничем не отличается, а результаты выполнения не зависят от используемой ОС.
Сначала проверим, установлен ли интерпретатор в системе или нет. В окне shell введите команду (этот ключ требует вывода версии языка). Если следующее сообщение появится, то
Файлы, содержащие программы на языке , обычно имеют расширение rb. По давней программистской традиции наша первая программа будет печатать фразу "Hello, World!". При помощи любого редактора plain-текста (emacs, kwrite, hello.rb, в который поместим текст
puts "Hello, World!"
Для выполнения этой программы в командной строке введите
В результате выполнения программы в командном окне будет напечатана требуемая фраза.
Второй способ выполнения программ доступен пользователям не всех операционных систем, в которых функционирует . Пользователям ОС Linux следует поместить в начало файла с текстом программы следующую строку:
#!/usr/bin/env
Она обязательно должна начинаться с первой позиции. Затем нужно изменить права доступа файла с программой, сделав его исполняемым: chmod +x hello.rb. Теперь для запуска программы достаточно ввести команду
./hello.rb
Для того чтобы сделать программу более понятной человеку, который ее читает, вставляются комментарии. Однострочные комментарии начинаются с символа # и продолжаются до конца строки. Многострочные комментарии заключают в специальные "скобки" - все, что располагается между строками =begin и =end, считается комментарием. Например,
#!/usr/bin/env ruby =begin Это комментарий =end puts "Hello, World!" # Это тоже комментарий
Программа на языке , часто называемая скриптом, есть последовательность инструкций (утверждений, предложений). Каждая инструкция по умолчанию заканчивается концом строки. Если же по каким-либо причинам требуется разместить несколько инструкций на одной строке, то их нужно разделять символом ; (точка с запятой). С другой стороны, иногда инструкция не помещается на одной строке. В этом случае символ \ сигнализирует о том, что ее продолжение располагается в следующей строке.
Пример
#!/usr/bin/env ruby
# Инструкция заканчивается концом строки
puts "Hello, World!"
# Несколько инструкций в одной строке
puts "Это тест, "; puts "демонстрирующий работу Ruby."
# Незаконченная инструкция,
# продолжение которой на следующей строке
puts "Программирование на Ruby - " +
"приятное занятие."
# Утверждение, разделенное на несколько строк
puts \
"И мы обязательно этому научимся!"
Инструкция (утверждение) чаще всего представляет собой некую последовательность операторов, применяемых к различным выражениям, и (возможно) вызовов функций.
Далее строка #!/usr/bin/env в тексты программ не включается.
При описании было отмечено, что он является объектно-ориентированным языком программирования. Это означает что все, с чем работает программа, является объектом, при этом вычисление осуществляется посредством взаимодействия объектов. Каждый объект есть представитель (экземпляр) некоторого класса и его поведение (функциональность) определяется именно им. Тем самым все объекты, которые являются экземплярами одного класса, могут выполнять одни и те же действия, называемые методами. Для того чтобы применить метод к некоторому объекту используется оператор вызова метода, обозначаемый символом . (точка): после указания объекта ставится точка, а затем указывается применяемый метод.
Пока мы не будем вдаваться в особенности объектно-ориентированного программирования (тем более, что в различных языках программирования по разному трактуется концепция
true - логическая величина, означающая истину; единственный представитель класса TrueClass ;
false - логическая величина, означающая ложь; единственный представитель класса FalseClass ;
числа - представители класса , который в качестве подклассов содержит дробные ( Float ) и целые числа ( Integer );
строки - представители класса String ;
- "ничто"; единственный представитель класса NilClass.
моменты времени - представители класса Time.
Знакомство с представителями различных классов начнем с чисел. Целые числа в являются экземплярами класса Integer, который объединяет два подкласса: Fixnum или . Объектами класса Fixnum являются те целые, чье , чей диапазон изменения ограничивается только объемом доступной памяти. Если в результате работы с объектами класса итоговое значение попадает в диапазон, задаваемый классом Fixnum, то выдаваемый результат преобразуется в экземпляр класса Fixnum.
| Число | Класс |
|---|---|
| 123456 | Fixnum |
| 123_456 | Fixnum (подчеркивание игнорируется) |
| -543 | отрицательное Fixnum |
| 123_456_789_123_456 | |
| 0xaabb | шестнадцатеричное |
| 0377 | восьмеричное |
| -0b1010 | двоичное (отрицательное) |
| 0b001_001 | двоичное |
При записи целых чисел сначала указывается его знак (знак + обычно опускается). Далее следует основание системы счисления, в которой задается число (если оно отлично от 10): 0 - для восьмеричной, 0x - для шестнадцатеричной, 0b - для двоичной. Затем идет последовательность цифр, выражающих число в данной системе счисления. При записи больших чисел можно использовать символ подчеркивания, который игнорируется при обработке. В таблице представлены целые числа и указана их принадлежность к тому или иному классу.
Дробные числа задаются в десятичной системе счисления, при этом для отделения дробной части используется символ . (точка). Такие числа являются экземплярами класса Float, например, 12.34. Для задания дробных чисел может быть применена и экспоненциальная форма записи: два различных представления -.1234е2 и 1234е-2 задают одно и тоже число 12.34.
Для вычисления арифметических выражений применяются следующие операторы: + (сложение), - (вычитание), * (умножение), / (деление), % (остаток от деления), ** (возведение в степень). Порядок вычисления определяется стандартными Float.
Пример
Создайте файл с именем object.rb, в который поместите следующий фрагмент. Выполните программу и объясните результат.
puts 5/8 # 0 puts 5.0/8 # 0.625 puts 2**1000 puts ((2*500+1)*(2**500-1))
#!/usr/bin/env ruby puts 5/8 # 0 puts 5.0/8 # 0.625 puts 2**1000 puts ((2*500+1)*(2**500-1)) =begin puts (2**30-1).type a=0b001_001 puts a puts a.type a=12.34 puts a puts a.type a=12.34e2 puts a puts a.type =end puts 12.34.ceil # 13 puts 12.34.floor # 12 puts -12.ceil # -12 puts -12.floor # -12 puts 12.34.round # 12 puts 12.54.round # 13 puts -34.56.abs # 34.56 puts -12.34.to_i # -12 puts 1234.to_f # 1234.0 =begin puts 123_456_789.type puts 0xaabb puts -0b1010/0b10 puts 2/3.to_f =end
Отметим еще несколько методов, используемых при работе с числами (т. е. представителями класса ). Среди них: (нахождение наименьшего целого не меньшего, чем данное), (наибольшее целое, не большее данного), round (округление до ближайшего целого), получение abs. Ниже приведены примеры использования этих методов.
puts 12.34.ceil # 13 puts 12.34.floor # 12 puts -12.ceil # -12 puts -12.floor # -12 puts 12.34.round # 12 puts 12.54.round # 13 puts -34.56.abs # 34.56
В имеется несколько методов, позволяющих преобразовывать объекты одного класса в другой.
| Метод | Назначение метода | Пример использования | Результат |
|---|---|---|---|
to_f |
Преобразовать объект в экземпляр класса Float |
1234.to_f |
1234.0 |
to_i |
Преобразовать объект в экземпляр класса Fixnum или |
-12.34.to_i |
-12 |
В последнем примере первая точка отделяет дробную часть числа, а вторая является оператором вызова метода.
Другим столь же распространенным классом является класс String. К этому классу относятся произвольные строки символов, заключенные в апострофы или кавычки, например, 'hello', 'раз, два, три', "привет всем". Есть несколько альтернативных способов задания каждого из этих видов строк.
Для задания строки в апострофах можно использовать любой из способов представленных ниже.
puts 'hello' # hello puts %q/hello/ # hello puts %q(hello) # hello
Два подряд идущих символа \ внутри такой строки заменяются на один.
puts 'hell\\o' # hell\o puts %q(hell\\o) # hell\o
Для того чтобы внутрь строки, заключенной в апострофы, вставить апостроф, следует предварительно "экранировать" его символом \ (обратный слеш).
puts 'hell\'o' # hell'o puts %q(hell\'o) # hell\'o puts 'hel"l"o' # hel"l"o
Для
"hello" %Q/hello/ %Q{hello} %Q(hello) %Q!hello!
%<hello> %(hello) %!hello! %*hello* %+hello+
puts "Say \"Hello\"" # Say "Hello"
Строка, заключенная в кавычки, позволяет интерпретировать последовательности символов, начинающиеся с символа \ такие, например, как \n (символ перехода на новую строку) и \t (табуляция). Другой особенностью строк, заключенных в кавычки, является возможность использования подстановки: если строка содержит некоторое выражение, ограниченное символами #{ и }, то оно заменяется на результат его вычисления. Добавьте в программу следующие строки и проанализируйте полученный вывод.
puts "a \t b"; puts 'a \t b'
puts "выражение 3*5+8 равно #{3*5+8}"
puts 'выражение 3*5+8 равно #{3*5+8}'
puts "работа с целыми числами: 5/8 = #{5/8}"
puts "перевод в класс Float: 5/8 = #{5/8.to_f}"
К строкам могут применяться методы to_i и to_f. При преобразовании к целому числу отбрасывается оставшаяся часть строки, как только встречается символ, отличный от цифры (исключение - знак плюс или минус в первой позиции строки). Аналогичные правила применяются и при преобразовании к дробному числу. Единственным отличием является то, что первая найденная точка рассматривается как разделитель между целой и дробной частями. Следующий фрагмент иллюстрирует сказанное:
puts "-12.34".to_i puts "12.34".to_f puts "+12:34".to_i puts "12qq34".to_f
Для получения строки, содержащей символ с заданным ASCII кодом, используется метод , например,
puts 209.
Следует помнить, что этот метод может быть применен только к положительному целому числу, не превышающему 255.
Класс String предоставляет большое число методов для работы со строками, некоторые из которых представлены в следующей таблице.
| Назначение и пример использования метода | Результат | |
|---|---|---|
| + | Сцепление строк "мол" + "око" | "молоко" |
| * | Повтор строки "ab" * 3 | "ababab" |
| [ ] | Возвращает ASCII-код символа, находящегося на указанной позиции строки (отсчет начинается с нуля)"abcdef"[0] "abcdef"[0].chr |
97 "a" |
| [нач..кон] | Возвращает подстроку, заключенную в указанном диапазоне (включая концы) "abcdef"[0..3] |
" |
| [нач, дл] | Возвращает подстроку, начинающуюся с указанной позиции и имеющую заданную длину "abcdef"[0,3] |
"abc" |
Заменяет первый символ строки (если он является буквой латинского алфавита) на заглавную "abc". |
"Abc" | |
| chop | Удаляет указанные символы из строки, может указываться диапазон изменения символов"abcdef".delete('ea')
"abcdef".delete('a-c')
|
"bcdf" "def" |
| index | Определяет номер позиции, с которой начинается указанная подстрока; можно указывать номер позиции, с которой начинается поиск"abcdabcd".index("cd")
"abcdabcd".index("cd",3)
|
2 6 |
| length size | Определяют длину строки (в байтах)"12345678".length "12345678".size |
8 8 |
| ljust rjust center | Дополняют строку пробелами до указанной ширины, выравнивая соответственно по левому краю, по правому краю или по центру"123".ljust(8) "123".rjust(8) "123".center(8) |
"123 " " 123" " 123 " |
| reverse | Возвращает строку, содержащую символы в обратном порядке "1234567".reverse |
"7654321" |
Удаляет пробелы в начале и конце строки " 123 ". |
"123" | |
Оставляет в группе повторяющихся символов только один; допускается задание списка символов, на которых распространяется данное действие"22---23**".squeeze
"22---23**".squeeze('*-')
|
"2-23*"
"22-23*" |
|
| tr | Заменяет все найденные вхождения символов на заданные "22+33=55".tr('25','47') |
"44+33=77" |
Отдельного упоминания заслуживает метод , позволяющий динамически выполнять другие методы. Этот метод анализирует переданную ему строку, рассматривая ее как часть программы, и выполняет ее. Обратите внимание, что при вызове этого метода используется не точечная, а функциональная форма записи:
puts eval("2**10")
puts eval('"мол".size * "око".size') # 9
#!/usr/bin/env ruby
puts 'hello' # hello
puts %q/hello/ # hello
puts %q(hello) # hello
puts 'hell\\o' # hell\o
puts %q(hell\\o) # hell\o
puts 'hell\'o' # hell'o
puts %q(hell\'o) # hell\'o
puts 'hel"l"o' # hel"l"o
puts "hello"
puts %Q/hello/
puts %Q!hello!
puts %Q{hello}
puts %Q(hello)
puts %Q!hello!
puts %<hello>
puts %!hello!
puts %*hello*
puts %+hello+
puts %(hello)
puts "Say \"Hello\"" # Say "Hello"
puts "Say 'Hello'" # Say 'Hello'
puts "a \t b"
puts 'a \t b'
puts "выражение 3*5+8 равно #{3*5+8}"
puts 'выражение 3*5+8 равно #{3*5+8}'
puts "работа с целыми числами: 5/8 = #{5/8}"
puts "перевод в класс Float: 5/8 = #{5/8.to_f}"
puts "-12.34".to_i
puts "12.34".to_f
puts "+12:34".to_i
puts "12qq34".to_f
puts 209.chr
puts "123456789".reverse
puts " 123 "
puts " 123 ".strip
puts "---22--23****".squeeze('*-')
puts "---22--23****".squeeze
puts "Дашка любит молоко, а Танька - квас".tr('ДТ','МД')
puts "22+33=55".tr('25','47')
puts "abcdef"[0].chr
puts "abcdef".chop
puts "abcdef".chop.chop
puts "abcdef".delete("ea")
puts "abcdef".delete('a-c')
puts "abcdefabcdef".length
puts "12345678".size
puts "ABCDEF".downcase
puts "abcdef".index("cd")
puts "abcdefabcdef".index("cd",3)
a="12345"
puts "12345"[3].chr
puts "12345"[0..3]
puts "12345"[0,3]
puts "привет".capitalize # Не работает с русскими буквами!
puts "abcdabcd".index("cd",3)
puts "*"+"123".ljust(9)+*"
puts "*"+"123".center(9)+"*"
puts "*"+"123".rjust(9)+"*"
puts (eval "2**10")
puts (eval ""мол".size * "око".size')
=begin
s="2*3+4"
puts s
puts "#{s}"
puts eval(s)
puts eval s
puts s.eval # Ошибка! Неправильный вызов метода
print "Введите команды:"
eval gets.chop!
=end
Экземпляр класса Time в языке содержит информацию о дате, времени и временной зоне. Для создания объекта этого класса, содержащего текущие дату и время, используется метод now. Такие объекты могут являться
puts Time.now puts Time.now+60 # добавили 60 секунд к текущему времени puts Time.now-60 # отняли 60 секунд от текущего времени
Отметим некоторые методы этого класса, для вызова которых используется
| Метод | Назначение метода |
|---|---|
sec |
Получить число секунд |
min |
Получить число минут |
hour |
Получить число часов |
mday и day |
Получить день месяца |
mon и month |
Получить номер месяца |
year |
Получить год |
wday |
Получить номер дня недели |
yday |
Получить номер дня в году |
|
Получить информацию о временной зоне |
to_i |
Получить число секунд, прошедших с 1 января 1970 года |
В языке имеется метод , который заставляет программу "заснуть" на число секунд, указанное в качестве аргумента метода. Посмотрите на пример использования методов для работы с объектами класса Time.
puts "До Нового года осталось #{365-Time.now.yday} дней"
puts Time.now
puts "Подождем 10 секунд."
sleep(10)
puts Time.now
#!/usr/bin/env ruby
puts Time.now
puts Time.now+60 # добавили 60 секунд к текущему времени
puts Time.now-60 # отняли 60 секунд от текущего времени
puts "До Нового года осталось #{365-Time.now.yday} дней"
puts Time.now
puts "Подождем 10 секунд."
sleep(10)
puts Time.now
Мы уже видели, как осуществляется вывод информации в . Познакомимся с другими операторами, обеспечивающими печать данных. Создайте файл print.rb, включите в него следующий текст и посмотрите на результат выполнения полученной программы.
puts "puts всегда завершается переходом к новой строке." print "А оператор print не делает этого по умолчанию, " print "как вы видите в этом примере.\n" print "print может быть вызван сразу ", "с несколькими аргументами.\n"
Оператор p подобен оператору puts и отображает объекты в виде, понятном человеку. Не следует использовать его для вывода русских букв - он печатает их ASCII-коды.
p Time.now, Time.now+3600
Значительно большими возможностями обладает оператор printf, обеспечивающий форматный вывод. Сразу после имени оператора указывается строка формата, содержащая как обычные символы, выводимые на печать, так и спецификации преобразования, которые вызывают преобразование и печать остальных аргументов в том порядке, в каком они перечислены. Каждая
- указывает на то, что преобразованный аргумент должен быть прижат к левому
+ предписывает всегда печатать число со знаком;
0 - указывает, что числа должны дополняться ведущими нулями до всей ширины поля;
пробел - указывает, что числа должны дополняться ведущими нулями до всей ширины поля;
Основные литеры-спецификаторы и разъяснение их смысла приводятся в следующей таблице.
| Литера | Аргумент | Вид печати |
|---|---|---|
| d | Fixnum, |
печать целого числа |
| s | String |
печать строки |
| f | Float |
печать дробного числа |
Включите в файл следующие операторы форматной печати и посмотрите на результаты выполнения:
printf "%8s~~%-8s\n", "abcd", "abcd"
printf "%-8s::%8s\n", "abcd", "abcd"
printf "%06d\n", 2**10; printf "%+6d\n", 2**10
printf "%6d\n", 2**10; printf "%+6d\n", 2.5*1.3
printf "%4.3f\n", 2.5*1.3
printf "-2/7=%+1.6f, -2/6=%2.15f\n",
-2/7.to_f, -2/6.to_f
Обратите внимание, что строка формата заключается в двойные кавычки и может содержать специальные последовательности символов. Одной из них является \n. Эта последовательность дает указание интерпретатору продолжить вывод информации со следующей строки (\t (табуляция), которая передвигает фокус вывода к следующей позиции табуляции.
puts "*\t*\t*\t*\t*" puts " 1234567 1234567 1234567 1234567 "
#!/usr/bin/env ruby
puts "puts всегда завершается переходом к новой строке."
print "А оператор print не делает этого по умолчанию, "
print "как вы видите в этом примере.\n"
print "print может быть вызван сразу ",
"с несколькими аргументами.\n"
p Time.now, Time.now+3600
printf "%8s~~%-8s\n", "abcd", "abcd"
printf "%-8s::%8s\n", "abcd", "abcd"
printf "%06d\n", 2**10; printf "%+6d\n", 2**10
printf "%6d\n", 2**10; printf "%+6d\n", 2.5*1.3
printf "%4.3f\n", 2.5*1.3
printf "-2/7=%+1.6f, -2/6=%2.15f\n",
-2/7.to_f, -2/6.to_f
puts "*\t*\t*\t*\t*"
puts " 1234567 1234567 1234567 1234567 "
В этом курсе мы будем использовать так называемые локальные переменные. Имена локальных переменных начинаются со строчной латинской буквы (от a до z) или символа подчеркивания и продолжаются любой комбинацией латинских букв, цифр и символа подчеркивания. Те же правила распространяются и на имена методов.
В языке имеется небольшое множество зарезервированных слов, которые нельзя использовать в качестве имен переменных и методов.
| Зарезервированные слова | |||||||
|---|---|---|---|---|---|---|---|
__FILE__ |
and |
def |
end |
in |
or |
self |
unless |
__LINE__ |
begin |
defined? |
ensure |
module |
|
super |
until |
BEGIN |
break |
do |
false |
next |
rescue |
then |
when |
END |
case |
else |
for |
|
|
true |
while |
|
class |
elsif |
if |
not |
return |
undef |
|
Кроме локальных переменных поддерживает и другие их виды - глобальные переменные, переменные классов и экземпляров. Принадлежность к каждому из этих видов определяются символом, стоящим перед именем переменной (префиксом): символ $ задает глобальную переменную, @ - @@ -
Локальная переменная создается динамически в момент, когда ей в процессе выполнения программы первый раз присваивается какое-то значение. Оператор присваивания имеет вид знака = (равно), например, a = 1; b = a; name = "Иван". Переменные могут использоваться во всех выражениях аналогично соответствующим объектам:
a = "Привет всем!" puts a
В переменные есть ссылки на объекты. Их следует трактовать как некие метки. Для того чтобы лучше понять эту концепцию проведем следующую аналогию: пусть у вас есть некоторая коробка (объект), вы берете наклейку (переменную), подписываете ее (даете имя переменной), после чего наклеиваете ее на коробку (объект). В переменные не имеют типа, так как они есть всего лишь ссылки на объекты произвольных типов. Проиллюстрируем сказанное следующим примером:
a = "one" puts a puts a.type a = 23 puts a puts a.type
Обратите внимание на метод type, возвращающий тип объекта. При первом использовании переменная a есть ссылка на объект класса String, затем мы присваиваем этой переменной другое значение (наклеиваем ту же этикетку на новый объект), после чего она становится ссылкой на экземпляр класса Fixnum.
поддерживает также и присваивание с операцией. Результат выполнения следующих двух операторов одинаковы:
a+=12 a=a+12
Общая форма подобных операторов выглядит так:
выражение1 операция= выражение2
что соответствует оператору
выражение1 = выражение1 операция выражение2
Между знаком операции и символом равенства не должно быть пробела. Такой синтаксис допустим для следующих операций:
+, -, *, /, %, **, , |, ^, <<, >>, , |
Когда требуется присвоить значение нескольким переменным, можно использовать множественное присваивание:
a, b, c = 1, 2, 3
Пример
Пусть требуется обменять значения двух переменных, не вводя вспомогательную переменную. С использованием множественного присваивания это можно записать так:
a, b = b, a
Данный пример объясняет, почему эту форму оператора присваивания называют также "параллельным присваиванием".
Мы уже упоминали одну из замечательных возможностей - подстановку. Она позволяет выполнять разнообразные трюки со строкой, заключенной в двойные кавычки, например,
magic = 42
puts "Секретное число равно #{magic - 3}!!"
Когда находит конструкцию #{выражение} внутри строки, заключенной в двойные кавычки, то заменяет эту ее часть на результат вычисления выражения.
Пример
Дано трехзначное число. Напечатать число, получающееся при перестановке цифр десятков и единиц заданного числа.
number = 342
puts "Исходное число равно #{number}"
n1 = number/100 # число сотен
n2 = (number/10)%10 # число десятков
n3 = number%10 # число единиц
answer = n1*100 + n3*10 + n2
puts "Результат #{answer}"
#!/usr/bin/env ruby
number = 342
puts "Исходное число равно #{number}"
n1 = number/100 # число сотен
n2 = (number/10)%10 # число десятков
n3 = number%10 # число единиц
answer = n1*100 + n3*10 + n2
puts "Результат #{answer}"
Иногда возникает необходимость указать, что тот или иной объект в программе является неизменяемым. Такие объекты называются константами. В имена констант, также как и имена классов, должны начинаться с заглавной буквы (есть определенная традиция задавать имена констант, состоящие из одних заглавных букв). Рассмотрим следующий фрагмент программы:
NUM = 234 p NUM # 234 NUM = "qq" # warning: already initialized constant NUM p NUM # "qq"
Как видим, интерпретатор выдал предупреждение о том, что константа NUM уже была определена ранее. Тем не менее значение константы было изменено.
Задания
Условный оператор, называемый также
поддерживает все конструкции выбора, традиционно используемые в других языках программирования. Но прежде чем говорить о них, остановимся на некоторых особенностях при вычислении истинности отдельных выражений.
Следует запомнить, что только false и трактуются как ложные в логическом контексте. Все остальное, что вычисляется, является истинным. Для того чтобы лучше понять смысл данного высказывания, выполните следующую программу:
if 23 puts "23 есть ИСТИНА" end if "qq" puts "И любая строка есть ИСТИНА" end
поддерживает все стандартные defined?. Для конъюнкции двух логических выражений используются and и . Их результат будет истинен только если инстинны оба операнда. Эти функции вычисляют второй операнд лишь тогда, когда первый истинен, а отличаются они только своим приоритетом (приоритет оператора and меньше ).
В свою очередь результатом выполнения операторов or и || (дизъюнкции) будет истина, если хотя бы один из операндов истинен. Они вычисляют второй оператор только в том случае, если первый окажется ложным. Как и в случае с оператором and, оператор or имеет меньший приоритет по сравнению с || (отметим, что оператор and имеет приоритет, равный приоритету оператора or, в то время как приоритет выше, чем у || ).
Операторы ! возвращают значение, противоположное своему аргументу (если аргумент истинен, то результат ложен, и наоборот). Между собой отличаются только приоритетом.
Оператор defined? возвращает , если его аргумент не определен, и его описание в противном случае. Ниже приведены примеры применения этого оператора.
puts defined? 1 puts defined? dummy puts defined? printf puts defined? (c, d = 1, 2) puts defined? 42.abs
В логических выражениях допускаются также следующие операции сравнения: == <, <=, >=, > !=.
Синтаксис условного оператора if в языке аналогичен его синтаксису в большинстве других языков программирования.
# Определение метода оценки величины выражения
def howBigPrint(i)
if i < 10
puts "Число #{i} меньше 10"
elsif i < 20
puts "Число #{i} между 10 и 20"
elsif i < 30
puts "Число #{i} между 20 и 30"
else
puts "Число #{i} больше или равно 30"
end
end
# Использование метода ...
howBigPrint(7); howBigPrint(15)
howBigPrint(23); howBigPrint(105)
Общая форма оператора if такова:
if <логическое_выражение> [then]
тело_оператора
elsif <логическое_выражение> [then]
тело_оператора
...
else
тело_оператора
end
Здесь <логическое_выражение> ; может быть любым фрагментом кода на языке , результатом вычисления которого является логическая величина (с учетом сказанного выше). Слово then отделяет if, есть результат последнего вычисленного выражения. Изменим наш пример, чтобы продемонстрировать сказанное:
# Метод оценки величины выражения # теперь возвращает строку def howBig(i) if i < 10 then "Аргумент меньше 10" elsif i < 20 then "Аргумент между 10 и 20" elsif i < 30 then "Аргумент между 20 и 30" else "Аргумент больше или равно 30" end end # Использование метода ... puts howBig(7); puts howBig(15) puts howBig(23); puts howBig(105)
Для выполнения одного варианта из двух возможных используется следующая форма оператора if:
if <логическое_выражение> [then]
тело_оператора
else
тело_оператора
end
В следующем фрагменте сравнивается значение переменной i с числом 100.
if i < 100 puts "Мало" else puts "Значительно больше" end
Оператор if может использоваться в правой части оператора присваивания, например,
str = if i < 100 then "Мало" else "Побольше" end puts str
Для аналогичных целей используется и тернарный оператор
логическое_выражение ? выражение1 : выражение2
Если логическое_выражение истинно, то выполняется выражение1, иначе - выражение2. Посмотрите на примеры использования этого оператора:
i = 23
i < 100 ? puts("Мало") : puts("Значительно больше")
i = 1234
s = i < 100 ? "Мало" : "Значительно больше"
puts s
Как видите, этот оператор особенно удобен, если требуется переменной присвоить одно из двух значений.
Пример
Напишите программу, определяющую четность введенного числа.
print "Введите целое число: "
a = gets.to_i
str = a%2 == 0 ? "четно" : "нечетно"
puts "Число #{a} " + str
Если требуется выполнить последовательность операторов только в том случае, когда выполнено некоторое условие, то используется форма
if <логическое_выражение> [then]
тело_оператора
end
Для этих же целей можно использовать и модифицированный оператор if:
выражение if <логическое_выражение>
Ниже приводятся две версии фрагмента программы, использующей оператор if:
if radiation > 3000 puts "Радиационная опасность!" end # то же самое puts "Радиационная опасность!" if radiation > 3000
Пример
Напишите программу, определяющую, является ли введенная фраза палиндромом (перевертышем).
puts "Введите фразу:"
a = gets.chop!.delete(' ') # удалили все пробелы
a = a.tr('A-Z,А-Я', 'a-z,а-я') # замена букв на прописные
str = "палиндром"
str = "не " + str if a != a.reverse # проверка
puts "Введенная фраза - " + str + "."
При выборе из большего числа альтернатив удобнее использовать оператор case. Ниже приведен его общий вид (части, заключенные в квадратные скобки, могут быть опущены).
case <выражение>
when <тест1> [then]
...
when <тест2> [then]
...
when <тестN> [then]
...
[else
... ]
end
В операторе case <выражение> последовательно сравнивается с выражениями <тест> до тех пор пока, оно не совпадет с одним из них (сравнение производится при помощи операции == ), после чего выполняется соответствующий фрагмент кода. Оператор case возвращает значение последнего вычисленного выражения, или , если такого не было.
s = case i
when 0 .. 9
"однозначное"
when 10 .. 99
"двузначное"
when 100 .. 999
"трехзначное"
else
"огромное"
end
puts "Введено #{s} число."
Здесь мы снова воспользовались оператором .. (двоеточие), который возвращает список целых чисел, заключенных между левым и правым операндами (включая их самих).
#!/usr/bin/env ruby
if 23
puts "23 есть ИСТИНА"
end
if "qq"
puts "И любая строка есть ИСТИНА"
end
puts
# Определение метода оценки величины выражения
def howBigPrint(i)
if i < 10
puts "Число #{i} меньше 10"
elsif i < 20
puts "Число #{i} между 10 и 20"
elsif i < 30
puts "Число #{i} между 20 и 30"
else
puts "Число #{i} больше или равно 30"
end
end
# Использование метода ...
howBigPrint(7); howBigPrint(15)
howBigPrint(23); howBigPrint(105)
puts
# Метод оценки величины выражения
# теперь возвращает строку
def howBig(i)
if i < 10 then "Аргумент меньше 10"
elsif i < 20 then "Аргумент между 10 и 20"
elsif i < 30 then "Аргумент между 20 и 30"
else "Аргумент больше или равно 30"
end
end
# Использование метода ...
puts howBig(7); puts howBig(15)
puts howBig(23); puts howBig(105)
puts
i = 23
i < 100 ? puts("Мало") : puts("Значительно больше")
i = 1234
s = i < 100 ? "Мало" : "Значительно больше"
puts s
puts "Перевертыш"
puts "Введите фразу:"
a = gets.chop!.delete(' ') # удалили все пробелы
a = a.tr('A-Z,А-Я', 'a-z,а-я') # замена букв на прописные
str = "палиндром"
str = "не " + str if a != a.reverse # проверка
puts "Введенная фраза - " + str + "."
print "Введите неотрицательное целое число: "
i= gets.to_i
s = case i
when 0 .. 9
"однозначное"
when 10 .. 99
"двузначное"
when 100 .. 999
"трехзначное"
else
"огромное"
end
puts "Введено #{s} число."
Задания
Для задания повторяющихся действий в большинстве языков программирования используются имеется два таких оператора - while и until, а также большое количество
Оператор while выполняет операторы, составляющее его тело, ноль или более раз, до тех пор, пока истинно его условие, задаваемое некоторым логическим выражением. Его общий вид таков:
while <выражение> [do]
...
тело цикла
...
end
Другим оператором цикла является until, выполняющийся до тех пор, пока его условие ложно:
until <выражение> [do]
...
тело цикла
...
end
Пример
Рассмотрим программу, печатающую числа от 1 до 5. Сначала используем оператор while, затем until. Обратите внимание, что условие окончания одного оператора цикла является отрицанием условия другого оператора.
i=1 while i <= 5 puts i; i += 1 end # еще раз i=1 until i > 5 puts i; i += 1 end
Кроме этих двух операторов цикла в имеется большое число, так называемых,
3.times do print "Ау! " end
использует times. Цикл, заданный таким образом, выполнится ровно три раза.
В случае, когда нужно выполнить некоторые действия, зависящие от изменяемой величины, несколько раз, можно использовать
0.upto(9) do |x| print x, " " end
будет напечатано 0 1 2 3 4 5 6 7 8 9.
Повтор от 0 до 12 с шагом 3 можно записать при помощи step:
0.step(12, 3) {|x| print x, " " } # 0 3 6 9 12
При работе с массивами удобно использовать each:
[1, 1, 2, 3].each {|k| print k, " " } # 1 1 2 3
for in очень похож на each, например, вывод, полученный в результате выполнения следующих двух конструкций одинаков.
for i in ["one", "two", "three"]
print i, " "
end
# то же самое
["one", "two", "three"].each{ |i| print i, " "}
for обычно используют там же, где и each - при работе с массивами и диапазонами. Общий вид оператора for таков:
for <переменная> in <выражение> [do] тело_итератора end
Пример
Перепишем программу печати чисел от 1 до 5 с использованием оператора for:
for i in 1 .. 5 puts i end
В этом примере мы снова воспользовались оператором задания диапазона .. (двоеточие), который позволил нам создать список чисел от 1 до 5 (включительно). Похожий оператор ... (троеточие) при создании диапазона не включает в него правый операнд. Фрагмент программы, расположенный ниже, эквивалентен предыдущему.
for i in 1 ... 6 puts i end
Пример
Рассмотрим несколько вариантов программы, вычисляющей факториал введенного числа.
whileprint "Введите целое положительное число: "
str = gets.chop! # ввели строку
num = str.to_i # преобразовали в число
if num > 0
i = 1
fact = 1
while i <= num
fact *= i
i += 1
end
puts "Факториал числа #{num} равен #{fact}"
else
puts "Вы ввели неположительное число"
end
forprint "Введите целое положительное число: "
num = gets.to_i # строку сразу преобразовали в число
if num > 0
fact = 1
for i in 1 .. num
fact *= i
end
puts "Факториал числа #{num} равен #{fact}"
else
puts "Вы ввели неположительное число"
end
def fact(n)
f = 1
1.step(n,1) {|k| f *= k}
return f
end
print "Введите целое положительное число: "
if (num = gets.to_i) > 0
print "#{num}! = #{fact(num)}\n"
else
puts "Факториал числа #{num} не определен\n"
end
В последнем примере мы оформили вычисление факториала в виде функции и использовали step, который обеспечил изменение переменной k с шагом 1.
Пример
Приведенная ниже программа запрашивает целое положительное число и определяет количество цифр в нем (обратите внимание на множественное присваивание).
print "Введите целое положительное число: "
a, k = gets.to_i, 0
while a>0
a /= 10; k += 1 # отбросили последнюю цифру
end
print "Количество цифр в введенном числе равно #{k}.\n"
Пример
Пусть нужно ввести с клавиатуры массив чисел и напечатать сумму всех элементов. Приведем несколько решений этой задачи.
print "Введите число элементов массива: "
sn = gets.chop!; n = sn.to_i
b = Array.new(n) # создали массив из n элементов
s = 0 # обнулили сумму
for i in 0 .. n - 1
print "Введите #{i+1}-й элемент массива: "
b[i] = gets.chop!.to_f; s = s + b[i]
end
print "Сумма всех элементов массива равна ", s, "\n"
Встроенный метод split разделяет строку на элементы массива, аргументом этого метода является разделитель (если разделителем является пробел, то можно вызвать этот метод без аргумента). Таким образом, если бы мы договорились разделять числа, например, точкой с запятой, то вызов метода выглядел бы так: a.split(';').
Для определения длины массива мы применили метод length (можно заменить на эквивалентный ему метод size ).
puts "Введите массив чисел (разделяя их пробелами):"
a = gets.chop!
b = a.split # разбили строку на отдельные числа
s = 0
for i in 0 .. b.length - 1
s += b[i].to_f
end
puts "Сумма всех элементов массива равна #{s}"
В break, , next и , которые изменяют обычный порядок выполнения циклов. Их действие описано в следующей таблице.
break |
Немедленно прекращает выполнение |
|
Повторяет тело цикла с начала, не пересчитывая условие выполнения цикла (не переходя к следующему элементу в случае |
next |
Пропускает часть тела цикла, следующую за ним, и переходит к следующей итерации |
|
Начинает выполнение цикла с самого начала |
Рассмотрим на примере for действие указанных конструкций.
for i in 1 .. 5 print i break if i == 3 print "*" end |
Результат: 1*2*3 |
for i in 1 .. 5 print i redo if i == 3 print "*" end |
Результат: 1*2*33333 ... выполнение цикла не прекращается |
for i in 1 .. 5 print i next if i == 3 print "*" end |
Результат: 1*2*34*5* |
for i in 1 .. 5 print i retry if i == 3 print "*" end |
Результат: 1*2*31*2*31*2*... выполнение цикла не прекращается |
Пример
Следующая программа начинает повторение цикла сначала, если при вводе указать символ y.
for i in 1 .. 5
print "Now at #{i}. Restart?(y/n) "
retry if gets.chop == "y"
end
Вот один из возможных вариантов выполнения этой программы:
Now at 1. Restart?(y/n) n
Now at 2. Restart?(y/n) y
Now at 1. Restart?(y/n) n
...
#!/usr/bin/env ruby
i=1
while i <= 5
puts i; i += 1
end
puts
# еще раз
i=1
until i > 5
puts i; i += 1
end
puts
3.times do
print "Ау! "
end
puts
0.upto(9) do |x| print x, " " end
puts
0.step(12, 3) {|x| print x, " " } # 0 3 6 9 12
puts
[1, 1, 2, 3].each {|k| print k, " " } # 1 1 2 3
puts
for i in ["one", "two", "three"]
print i, " "
end
puts
# то же самое
["one", "two", "three"].each{ |i| print i, " "}
puts
for i in 1..5
puts i
end
puts
for i in 1 ... 6
puts i
end
puts
print "Введите целое положительное число: "
str = gets.chop! # ввели строку
num = str.to_i # преобразовали в число
if num > 0
i = 1
fact = 1
while i <= num
fact *= i
i += 1
end
puts "Факториал числа #{num} равен #{fact}"
else
puts "Вы ввели неположительное число"
end
puts
print "Введите целое положительное число: "
num = gets.to_i # строку сразу преобразовали в число
if num > 0
fact = 1
for i in 1 .. num
fact *= i
end
puts "Факториал числа #{num} равен #{fact}"
else
puts "Вы ввели неположительное число"
end
puts
def fact(n)
f = 1
1.step(n,1) {|k| f *= k}
return f
end
print "Введите целое положительное число: "
if (num = gets.to_i) > 0
print "#{num}! = #{fact(num)}\n"
else
puts "Факториал числа #{num} не определен\n"
end
puts
print "Введите целое положительное число: "
a, k = gets.to_i, 0
while a>0
a /= 10; k += 1 # отбросили последнюю цифру
end
print "Количество цифр в введенном числе равно #{k}.\n"
puts
print "Введите число элементов массива: "
sn = gets.chop!; n = sn.to_i
b = Array.new(n) # создали массив из n элементов
s = 0 # обнулили сумму
for i in 0 .. n - 1
print "Введите #{i+1}-й элемент массива: "
b[i] = gets.chop!.to_f; s = s + b[i]
end
print "Сумма всех элементов массива равна ", s, "\n"
puts
puts "Введите массив чисел (разделяя их пробелами):"
a = gets.chop!
b = a.split # разбили строку на отдельные числа
s = 0
for i in 0 .. b.length - 1
s += b[i].to_f
end
puts "Сумма всех элементов массива равна #{s}"
puts
=begin
for i in 1 .. 5
print i
break if i == 3
print "*"
end
puts
for i in 1 .. 5
print i
redo if i == 3
print "*"
end
puts
for i in 1 .. 5
print i
next if i == 3
print "*"
end
puts
for i in 1 .. 5
print i
retry if i == 3
print "*"
end
puts
=end
for i in 1 .. 5
print "Now at #{i}. Restart?(y/n) "
retry if gets.chop == "y"
end
puts
Задания
При рассмотрении задач, связанных с обработкой целых чисел, часто приходится сталкиваться с понятием НОД - наибольшего общего делителя чисел. Известно много алгоритмов вычисления НОД, мы рассмотрим лишь два из них.
Задача
Напишите программу, вычисляющую НОД(a,b) - наибольший общий делитель двух введенных с клавиатуры неотрицательных целых чисел a и b, не равных нулю одновременно.
Вариант 1
Определим максимум из этих чисел и, последовательно уменьшая его, будем искать число, на которое делятся и a и b. Программа обязательно завершит свою работу, так как в самом неудачном случае, когда эти числа взаимно просты, k станет равно 1, а на 1 делятся все числа, следовательно выполнение цикла прекратится.
print "Введите первое число: "; a = gets.to_i
print "Введите второе число: "; b = gets.to_i
k = a >= b ? a : b # теперь k - максимум
until (a % k == 0) and (b % k == 0)
k -= 1
end
print "НОД(#{a},#{b}) = #{k}\n"
Вариант 2 - алгоритм Евклида
НОД: для всех a и b, больших или равных 0, выполнены равенства
НОД(a, b) = НОД(a - b, b)= НОД(a, b - a); НОД(a, 0) = НОД(0, a) = a
Ниже приведен текст программы, реализующей
print "Введите первое число: "; a = gets.to_i
print "Введите второе число: "; b = gets.to_i
m, n = a, b
while !((m == 0) || (n == 0))
if m >= n
m = m - n
else
n = n - m
end
end
k = m == 0 ? n : m
print "НОД(#{a},#{b}) = #{k}\n"
#!/usr/bin/env ruby
# 1
print "Введите первое число: "; a = gets.to_i
print "Введите второе число: "; b = gets.to_i
k = a >= b ? a : b # теперь k - максимум
until (a%k == 0) (b%k == 0)
k-=1
end
print "НОД(#{a},#{b}) = #{k}\n"
#2 Алгоритм Евклида
print "Введите первое число: "; a = gets.to_i
print "Введите второе число: "; b = gets.to_i
m, n = a, b
while !((m == 0) || (n == 0))
if m >= n
m = m - n
else
n = n - m
end
end
k = m == 0 ? n : m
print "НОД(#{a},#{b}) = #{k}\n"
Как и другие языки программирования, позволяет передавать данные в программу, использую
=begin
Напишите программу подсчета суммы всех нечетных
чисел, заключенных в интервале от K до L,
где K и L - аргументы командной строки
=end
if ARGV.length < 2
puts "Слишком мало входных данных"
exit(1)
end
s = 0
k = ARGV[0].to_i
l = ARGV[1].to_i
for i in k .. l
s += i if i%2 != 0
end
puts "Сумма нечетных чисел в интервале " +
"от #{k} до #{l} равна #{s}"
Для экстренного прерывания процесса выполнения программы здесь использован встроенный метод exit.
#!/usr/bin/env ruby
=begin
Напишите программу подсчета суммы всех нечетных
чисел, заключенных в интервале от K до L,
где K и L - аргументы командной строки
=end
if ARGV.length < 2
puts "Слишком мало входных данных"
exit(1)
end
s = 0
k = ARGV[0].to_i
l = ARGV[1].to_i
for i in k .. l
s += i if i%2 != 0
end
puts "Сумма нечетных чисел в интервале " +
"от #{k} до #{l} равна #{s}"
Задача
n, и печатающую n -е
Вариант 1 - рекурсивная функция
В следующей программе первый элемент массива ARGV преобразовывается в целое число и передается в качестве аргумента рекурсивной функции .
def fib(n)
if n<2
n
else
fib(n-2)+fib(n-1)
end
end
n = ARGV[0].to_i
puts "#{n}-е число Фибоначчи равно #{fib(n)}"
Для вычисления с помощью этой программы, размещенной в файлe , например, 30-го
Данная программа использует двойную рекурсию, поэтому процесс вычисления займет достаточно заметное время.
#!/usr/bin/env ruby
=begin
Напишите программу, получающую с качестве аргумента командной строки целое
число n, и печатающую n-е число Фибоначчи.
=end
def fib(n)
if n<2
n
else
fib(n-2)+fib(n-1)
end
end
t1 = Time.now
n = ARGV[0].to_i
puts "#{n}-е число Фибоначчи равно #{fib(n)}"
t2 = Time.now
puts "Время расчета около #{(t2-t1).round} сек."
Вариант 2 - использование массива
В следующем варианте решения этой задачи каждое
n =ARGV[0].to_i
f= [0, 1]
fib = case n
when 0
f[0]
when 1
f[1]
else
for i in 2 .. n
f[i] = f[i-1] + f[i-2]
end
f[n]
end
puts "#{n}-е число Фибоначчи равно #{fib}"
#!/usr/bin/env ruby
=begin
Напишите программу, получающую с качестве аргумента командной строки целое
число n, и печатающую n-е число Фибоначчи.
=end
t1 = Time.now
n =ARGV[0].to_i
f= [0, 1]
fib = case n
when 0
f[0]
when 1
f[1]
else
for i in 2 .. n
f[i] = f[i-1] + f[i-2]
end
f[n]
end
puts "#{n}-е число Фибоначчи равно #{fib}"
t2 = Time.now
puts "Время расчета около #{(t2-t1).round} сек."
a = fib.to_s.split('")
puts "Количество цифр в #{n}-м числе Фибоначчи равно #{a.size}."
Однако и у этой программы есть существенный недостаток - она хранит все промежуточные результаты, в то время как каждое предыдущее значение используется лишь дважды. Изменим программу так, чтобы она использовала только три переменных: вычисляемое значение и два предшествующих ему:
n =ARGV[0].to_i
if n > 1
prev, beforePrev = 1, 0
for i in 2 .. n
fib = prev + beforePrev
prev, beforePrev = fib, prev
end
else
fib = n
end
puts "#{n}-е число Фибоначчи равно #{fib}"
#!/usr/bin/env ruby
=begin
Напишите программу, получающую с качестве аргумента командной строки целое
число n, и печатающую n-е число Фибоначчи.
=end
t1 = Time.now
n =ARGV[0].to_i
if n > 1
prev, beforePrev = 1, 0
for i in 2 .. n
fib = prev + beforePrev
prev, beforePrev = fib, prev
end
else
fib = n
end
puts "#{n}-е число Фибоначчи равно #{fib}"
t2 = Time.now
puts "Время расчета около #{(t2-t1).round} сек."
a = fib.to_s.split("')
puts "Количество цифр в #{n}-м числе Фибоначчи равно #{a.size}."
Включите в рассмотренные программы операторы, информирующие о продолжительности вычислений, и сравните их эффективность. Для этого найдите с помощью первой из рассмотренных программ 30, 31 и 32
Ввод данных с клавиатуры в процессе выполнения программы удобен только в случае небольших объемов. В других ситуациях необходимо чтение информации из заранее подготовленного файла.
Для работы с файлом при помощи метода new создается экземпляр класса File. Обязательным аргументом этого метода является строка, содержащая имя файла, например,
f = File.new("myfile.txt")
Другим (необязательным) аргументом является задание режима работы с файлом. По умолчанию этот параметр имеет значение " r ", что соответствует режиму "только чтение". Если требуется открыть файл с возможностью записи в него, то следует указать параметр " w ", например,
f1 = File.new("newfile.txt", "w")
Метод readlines считывает весь файл, создает массив и размещает каждую прочитанную строку в отдельном элементе массива При дальнейшей обработке полученного массива удобно использовать метод each.
Для вывода строки в выходной поток (в файл или на экран монитора) используется метод write, например,
a = "Hello, world!" f1.write(a)
Задача
Имеется текстовый файл fio.txt, содержащий список фамилий, имен и отчеств учащихся. При этом каждая строка файла содержит данные только об одном человеке, например,
Петров Сергей Васильевич Сидорова Ольга Петровна Иванова Марья Даниловна
Напишите программу, которая читает информацию из файла и
Решение 1
f = File.new("fio.txt")
n = 1
student = f.readlines
student.each{ |i|
print n, ". ", i
n += 1
}
Отметим, что программа должна располагаться в той же директории, что и файл с данными. В противном случае необходимо указывать полный путь к этому файлу.
Для получения инициалов каждую строку полученного массива преобразуем в массив, элементами которого будут фамилия, имя и отчество. Затем фамилию печатаем полностью, а вместо следующих элементов массива - только их первый символ. Для преобразования строки в массив используется ранее рассмотренная функция split. Напомним, что параметром по умолчанию этой функции является пробел.
Решение 2
f = File.new("fio.txt")
n = 1
student = f.readlines
student.each{ |i|
i.chop!
fio = i.split
print n, ". ", fio[0], " ", fio[0][0].chr, ". ",
fio[1][0].chr, ".\n"
n += 1
}
#!/usr/bin/env ruby
# 1
f = File.new("fio.txt")
n = 1
student = f.readlines
student.each{ |i|
print n, ". ", i
n += 1
}
puts
# 2
f = File.new("fio.txt")
n = 1
student = f.readlines
student.each{ |i|
i.chop!
fio = i.split
print n, ". ", fio[0], " ", fio[0][0].chr, ". ",
fio[1][0].chr, ".\n"
n += 1
}
Моделирование случайных процессов на компьютере - достаточно распространенное явление. Как и в большинстве языков программирования в есть методы, генерирующие, так называемые, "псевдослучайные" числа (их нельзя считать чисто случайными, ведь создаются они при помощи некоторого алгоритма).
Для получения случайного целого числа в диапазоне от 0 до n используется метод rand(n). Случайное число в диапазоне от 0 до 1 получается с помощью этого же метода без указания параметра.
a1 = (1..10).collect { |j| rand(100)}
a2 = (1..4).collect { |j| rand()}
p a1, a2
Ниже представлен возможный вывод:
[64, 17, 21, 95, 58, 24, 29, 60, 47, 63] [0.2009671596, 0.8890923676, 0.3349569312, 0.8719313448]
Одним из применений случайных чисел является
Задача
Напишите программу, вычисляющее приближенное значение числа PI при помощи
Решение
Число PI равно отношению площади круга к квадрату его радиуса. Впишем круг единичного радиуса с центром в начале координат в квадрат и методом Монте-Карло найдем его площадь. Координаты случайных точек, бросаемых в круг, принадлежат интервалу (-1; 1). Величина, случайно распределенная в этом интервале, задается выражением 2*rand() - 1.
Для контроля за временем добавим два экземпляра класса Time: время начала и окончания расчета. Программа, реализующая этот алгоритм, представлена ниже. В ней используется оператор , чтобы количество точек можно было задавать в виде арифметического выражения.
puts "Введите количество точек:"
n1, n, t1 = 0, eval(gets.chop), Time.now
for i in 1 .. n
x = 2*rand() - 1
y = 2*rand() - 1
# проверяем попадание внутрь круга
n1 += 1 if (x**2 + y**2) < 1
end
puts "PI=#{4.0*n1/n}"
t2 = Time.now
puts "Число точек #{n}, время расчета " +
"около #{(t2 - t1).round} сек."
Возможные результаты работы программы будут похожи (случайность!) на следующие:
Введите количество точек: 10**5 PI=3.14028 Число точек 100000, время расчета около 2 сек. Введите количество точек: 10**6 PI=3.142204 Число точек 1000000, время расчета около 20 сек.
#!/usr/bin/env ruby
# Метод Монте-Карло
puts "Введите количество точек:"
n1, n, t1 = 0, eval(gets.chop), Time.now
for i in 1 .. n
x = 2*rand() - 1
y = 2*rand() - 1
# проверяем попадание внутрь круга
n1 += 1 if (x**2 + y**2) < 1
end
puts "PI=#{4.0*n1/n}"
t2 = Time.now
puts "Число точек #{n}, время расчета " +
"около #{(t2 - t1).round} сек."
Пример программы на языке , демонстрирующий работу с классами, уже приводился в глава 9. Следующий пример демонстрирует использование так называемой переменной класса, которая не принадлежит ни одному из экземпляров данного класса, а используется для хранения информации, относящейся ко всему классу в целом.
Задача
Напишите программу, описывающую класс автомобиль и два его дочерних класса: спортивные машины и машины для семейного использования. В программе нужно предусмотреть возможность печати общего числа созданных автомобилей.
Решение
Поместите в файл с именем car.rb следующую программу:
class Car
@@NUM_CARS = 0
def initialize
@@NUM_CARS = @@NUM_CARS + 1
puts @@NUM_CARS
end
end
class SportsCar < Car
end
class FamilyCar < Car
end
a = Car.new
b = SportsCar.new
c = FamilyCar.new
Для запуска программы выполните команду . При создании каждого автомобиля, независимо от того, к какому (родительскому или дочернему) классу он принадлежит, значение переменной класса @@NUM_CARS увеличивается на единицу.
#!/usr/bin/env ruby
class Car
@@NUM_CARS = 0
def initialize
@@NUM_CARS = @@NUM_CARS + 1
puts @@NUM_CARS
end
end
class SportsCar < Car
end
class FamilyCar < Car
end
a = Car.new
b = SportsCar.new
c = FamilyCar.new
Задания
НОД.y=sin(x) и y=0 ( x изменяется в интервале от 0 до PI ). - один из самых молодых языков программирования. Своим именем он обязан драгоценному камню рубину (по аналогии с другим широко распространенным языком программирования Perl - жемчуг). Вот как описывает его создатель, японский программист Юкихиро Мацумото (Yukihiro Matsumoto): "Это мощный и динамический объектно-ориентированный язык с открытыми исходниками, который я начал разрабатывать в 1993 году.
В Японии сильно потеснил такие известные языки как и Perl (а книга " (к сожалению, пока не имеющие русского перевода). У этого языка очень неплохие шансы стать действительно популярным - ведь он вобрал в себя достоинства других языков, учтя их недостатки.
входит в стандартную поставку ОС GNU\Linux (начиная с версии 7.2), а пользователям MS Windows для первого знакомства стоит порекомендовать его несколько устаревшую версию (1,2 Мбайт), легко умещающуюся на одной дискете, которая включает, помимо интерпретатора языка и библиотек, руководство пользователя, является свободно распространяемым продуктом, поэтому вы можете не беспокоиться ни о его стоимости, ни об ограничениях в его использовании.
Этот язык несомненно является одним из лучших в качестве первого языка программирования, изучаемого студентами и школьниками. Быстрый в других операционных системах практически ничем не отличается, а результаты выполнения не зависят от используемой ОС.
Сначала проверим, установлен ли интерпретатор в системе или нет. В окне shell введите команду (этот ключ требует вывода версии языка). Если следующее сообщение появится, то
Файлы, содержащие программы на языке , обычно имеют расширение rb. По давней программистской традиции наша первая программа будет печатать фразу "Hello, World!". При помощи любого редактора plain-текста (emacs, kwrite, hello.rb, в который поместим текст
puts "Hello, World!"
Для выполнения этой программы в командной строке введите
В результате выполнения программы в командном окне будет напечатана требуемая фраза.
Второй способ выполнения программ доступен пользователям не всех операционных систем, в которых функционирует . Пользователям ОС Linux следует поместить в начало файла с текстом программы следующую строку:
#!/usr/bin/env
Она обязательно должна начинаться с первой позиции. Затем нужно изменить права доступа файла с программой, сделав его исполняемым: chmod +x hello.rb. Теперь для запуска программы достаточно ввести команду
./hello.rb
Для того чтобы сделать программу более понятной человеку, который ее читает, вставляются комментарии. Однострочные комментарии начинаются с символа # и продолжаются до конца строки. Многострочные комментарии заключают в специальные "скобки" - все, что располагается между строками =begin и =end, считается комментарием. Например,
#!/usr/bin/env ruby =begin Это комментарий =end puts "Hello, World!" # Это тоже комментарий
Программа на языке , часто называемая скриптом, есть последовательность инструкций (утверждений, предложений). Каждая инструкция по умолчанию заканчивается концом строки. Если же по каким-либо причинам требуется разместить несколько инструкций на одной строке, то их нужно разделять символом ; (точка с запятой). С другой стороны, иногда инструкция не помещается на одной строке. В этом случае символ \ сигнализирует о том, что ее продолжение располагается в следующей строке.
Пример
#!/usr/bin/env ruby
# Инструкция заканчивается концом строки
puts "Hello, World!"
# Несколько инструкций в одной строке
puts "Это тест, "; puts "демонстрирующий работу Ruby."
# Незаконченная инструкция,
# продолжение которой на следующей строке
puts "Программирование на Ruby - " +
"приятное занятие."
# Утверждение, разделенное на несколько строк
puts \
"И мы обязательно этому научимся!"
Инструкция (утверждение) чаще всего представляет собой некую последовательность операторов, применяемых к различным выражениям, и (возможно) вызовов функций.
Далее строка #!/usr/bin/env в тексты программ не включается.
При описании было отмечено, что он является объектно-ориентированным языком программирования. Это означает что все, с чем работает программа, является объектом, при этом вычисление осуществляется посредством взаимодействия объектов. Каждый объект есть представитель (экземпляр) некоторого класса и его поведение (функциональность) определяется именно им. Тем самым все объекты, которые являются экземплярами одного класса, могут выполнять одни и те же действия, называемые методами. Для того чтобы применить метод к некоторому объекту используется оператор вызова метода, обозначаемый символом . (точка): после указания объекта ставится точка, а затем указывается применяемый метод.
Пока мы не будем вдаваться в особенности объектно-ориентированного программирования (тем более, что в различных языках программирования по разному трактуется концепция
true - логическая величина, означающая истину; единственный представитель класса TrueClass ;
false - логическая величина, означающая ложь; единственный представитель класса FalseClass ;
числа - представители класса , который в качестве подклассов содержит дробные ( Float ) и целые числа ( Integer );
строки - представители класса String ;
- "ничто"; единственный представитель класса NilClass.
моменты времени - представители класса Time.
Знакомство с представителями различных классов начнем с чисел. Целые числа в являются экземплярами класса Integer, который объединяет два подкласса: Fixnum или . Объектами класса Fixnum являются те целые, чье , чей диапазон изменения ограничивается только объемом доступной памяти. Если в результате работы с объектами класса итоговое значение попадает в диапазон, задаваемый классом Fixnum, то выдаваемый результат преобразуется в экземпляр класса Fixnum.
| Число | Класс |
|---|---|
| 123456 | Fixnum |
| 123_456 | Fixnum (подчеркивание игнорируется) |
| -543 | отрицательное Fixnum |
| 123_456_789_123_456 | |
| 0xaabb | шестнадцатеричное |
| 0377 | восьмеричное |
| -0b1010 | двоичное (отрицательное) |
| 0b001_001 | двоичное |
При записи целых чисел сначала указывается его знак (знак + обычно опускается). Далее следует основание системы счисления, в которой задается число (если оно отлично от 10): 0 - для восьмеричной, 0x - для шестнадцатеричной, 0b - для двоичной. Затем идет последовательность цифр, выражающих число в данной системе счисления. При записи больших чисел можно использовать символ подчеркивания, который игнорируется при обработке. В таблице представлены целые числа и указана их принадлежность к тому или иному классу.
Дробные числа задаются в десятичной системе счисления, при этом для отделения дробной части используется символ . (точка). Такие числа являются экземплярами класса Float, например, 12.34. Для задания дробных чисел может быть применена и экспоненциальная форма записи: два различных представления -.1234е2 и 1234е-2 задают одно и тоже число 12.34.
Для вычисления арифметических выражений применяются следующие операторы: + (сложение), - (вычитание), * (умножение), / (деление), % (остаток от деления), ** (возведение в степень). Порядок вычисления определяется стандартными Float.
Пример
Создайте файл с именем object.rb, в который поместите следующий фрагмент. Выполните программу и объясните результат.
puts 5/8 # 0 puts 5.0/8 # 0.625 puts 2**1000 puts ((2*500+1)*(2**500-1))
#!/usr/bin/env ruby puts 5/8 # 0 puts 5.0/8 # 0.625 puts 2**1000 puts ((2*500+1)*(2**500-1)) =begin puts (2**30-1).type a=0b001_001 puts a puts a.type a=12.34 puts a puts a.type a=12.34e2 puts a puts a.type =end puts 12.34.ceil # 13 puts 12.34.floor # 12 puts -12.ceil # -12 puts -12.floor # -12 puts 12.34.round # 12 puts 12.54.round # 13 puts -34.56.abs # 34.56 puts -12.34.to_i # -12 puts 1234.to_f # 1234.0 =begin puts 123_456_789.type puts 0xaabb puts -0b1010/0b10 puts 2/3.to_f =end
Отметим еще несколько методов, используемых при работе с числами (т. е. представителями класса ). Среди них: (нахождение наименьшего целого не меньшего, чем данное), (наибольшее целое, не большее данного), round (округление до ближайшего целого), получение abs. Ниже приведены примеры использования этих методов.
puts 12.34.ceil # 13 puts 12.34.floor # 12 puts -12.ceil # -12 puts -12.floor # -12 puts 12.34.round # 12 puts 12.54.round # 13 puts -34.56.abs # 34.56
В имеется несколько методов, позволяющих преобразовывать объекты одного класса в другой.
| Метод | Назначение метода | Пример использования | Результат |
|---|---|---|---|
to_f |
Преобразовать объект в экземпляр класса Float |
1234.to_f |
1234.0 |
to_i |
Преобразовать объект в экземпляр класса Fixnum или |
-12.34.to_i |
-12 |
В последнем примере первая точка отделяет дробную часть числа, а вторая является оператором вызова метода.
Другим столь же распространенным классом является класс String. К этому классу относятся произвольные строки символов, заключенные в апострофы или кавычки, например, 'hello', 'раз, два, три', "привет всем". Есть несколько альтернативных способов задания каждого из этих видов строк.
Для задания строки в апострофах можно использовать любой из способов представленных ниже.
puts 'hello' # hello puts %q/hello/ # hello puts %q(hello) # hello
Два подряд идущих символа \ внутри такой строки заменяются на один.
puts 'hell\\o' # hell\o puts %q(hell\\o) # hell\o
Для того чтобы внутрь строки, заключенной в апострофы, вставить апостроф, следует предварительно "экранировать" его символом \ (обратный слеш).
puts 'hell\'o' # hell'o puts %q(hell\'o) # hell\'o puts 'hel"l"o' # hel"l"o
Для
"hello" %Q/hello/ %Q{hello} %Q(hello) %Q!hello!
%<hello> %(hello) %!hello! %*hello* %+hello+
puts "Say \"Hello\"" # Say "Hello"
Строка, заключенная в кавычки, позволяет интерпретировать последовательности символов, начинающиеся с символа \ такие, например, как \n (символ перехода на новую строку) и \t (табуляция). Другой особенностью строк, заключенных в кавычки, является возможность использования подстановки: если строка содержит некоторое выражение, ограниченное символами #{ и }, то оно заменяется на результат его вычисления. Добавьте в программу следующие строки и проанализируйте полученный вывод.
puts "a \t b"; puts 'a \t b'
puts "выражение 3*5+8 равно #{3*5+8}"
puts 'выражение 3*5+8 равно #{3*5+8}'
puts "работа с целыми числами: 5/8 = #{5/8}"
puts "перевод в класс Float: 5/8 = #{5/8.to_f}"
К строкам могут применяться методы to_i и to_f. При преобразовании к целому числу отбрасывается оставшаяся часть строки, как только встречается символ, отличный от цифры (исключение - знак плюс или минус в первой позиции строки). Аналогичные правила применяются и при преобразовании к дробному числу. Единственным отличием является то, что первая найденная точка рассматривается как разделитель между целой и дробной частями. Следующий фрагмент иллюстрирует сказанное:
puts "-12.34".to_i puts "12.34".to_f puts "+12:34".to_i puts "12qq34".to_f
Для получения строки, содержащей символ с заданным ASCII кодом, используется метод , например,
puts 209.
Следует помнить, что этот метод может быть применен только к положительному целому числу, не превышающему 255.
Класс String предоставляет большое число методов для работы со строками, некоторые из которых представлены в следующей таблице.
| Назначение и пример использования метода | Результат | |
|---|---|---|
| + | Сцепление строк "мол" + "око" | "молоко" |
| * | Повтор строки "ab" * 3 | "ababab" |
| [ ] | Возвращает ASCII-код символа, находящегося на указанной позиции строки (отсчет начинается с нуля)"abcdef"[0] "abcdef"[0].chr |
97 "a" |
| [нач..кон] | Возвращает подстроку, заключенную в указанном диапазоне (включая концы) "abcdef"[0..3] |
" |
| [нач, дл] | Возвращает подстроку, начинающуюся с указанной позиции и имеющую заданную длину "abcdef"[0,3] |
"abc" |
Заменяет первый символ строки (если он является буквой латинского алфавита) на заглавную "abc". |
"Abc" | |
| chop | Удаляет указанные символы из строки, может указываться диапазон изменения символов"abcdef".delete('ea')
"abcdef".delete('a-c')
|
"bcdf" "def" |
| index | Определяет номер позиции, с которой начинается указанная подстрока; можно указывать номер позиции, с которой начинается поиск"abcdabcd".index("cd")
"abcdabcd".index("cd",3)
|
2 6 |
| length size | Определяют длину строки (в байтах)"12345678".length "12345678".size |
8 8 |
| ljust rjust center | Дополняют строку пробелами до указанной ширины, выравнивая соответственно по левому краю, по правому краю или по центру"123".ljust(8) "123".rjust(8) "123".center(8) |
"123 " " 123" " 123 " |
| reverse | Возвращает строку, содержащую символы в обратном порядке "1234567".reverse |
"7654321" |
Удаляет пробелы в начале и конце строки " 123 ". |
"123" | |
Оставляет в группе повторяющихся символов только один; допускается задание списка символов, на которых распространяется данное действие"22---23**".squeeze
"22---23**".squeeze('*-')
|
"2-23*"
"22-23*" |
|
| tr | Заменяет все найденные вхождения символов на заданные "22+33=55".tr('25','47') |
"44+33=77" |
Отдельного упоминания заслуживает метод , позволяющий динамически выполнять другие методы. Этот метод анализирует переданную ему строку, рассматривая ее как часть программы, и выполняет ее. Обратите внимание, что при вызове этого метода используется не точечная, а функциональная форма записи:
puts eval("2**10")
puts eval('"мол".size * "око".size') # 9
#!/usr/bin/env ruby
puts 'hello' # hello
puts %q/hello/ # hello
puts %q(hello) # hello
puts 'hell\\o' # hell\o
puts %q(hell\\o) # hell\o
puts 'hell\'o' # hell'o
puts %q(hell\'o) # hell\'o
puts 'hel"l"o' # hel"l"o
puts "hello"
puts %Q/hello/
puts %Q!hello!
puts %Q{hello}
puts %Q(hello)
puts %Q!hello!
puts %<hello>
puts %!hello!
puts %*hello*
puts %+hello+
puts %(hello)
puts "Say \"Hello\"" # Say "Hello"
puts "Say 'Hello'" # Say 'Hello'
puts "a \t b"
puts 'a \t b'
puts "выражение 3*5+8 равно #{3*5+8}"
puts 'выражение 3*5+8 равно #{3*5+8}'
puts "работа с целыми числами: 5/8 = #{5/8}"
puts "перевод в класс Float: 5/8 = #{5/8.to_f}"
puts "-12.34".to_i
puts "12.34".to_f
puts "+12:34".to_i
puts "12qq34".to_f
puts 209.chr
puts "123456789".reverse
puts " 123 "
puts " 123 ".strip
puts "---22--23****".squeeze('*-')
puts "---22--23****".squeeze
puts "Дашка любит молоко, а Танька - квас".tr('ДТ','МД')
puts "22+33=55".tr('25','47')
puts "abcdef"[0].chr
puts "abcdef".chop
puts "abcdef".chop.chop
puts "abcdef".delete("ea")
puts "abcdef".delete('a-c')
puts "abcdefabcdef".length
puts "12345678".size
puts "ABCDEF".downcase
puts "abcdef".index("cd")
puts "abcdefabcdef".index("cd",3)
a="12345"
puts "12345"[3].chr
puts "12345"[0..3]
puts "12345"[0,3]
puts "привет".capitalize # Не работает с русскими буквами!
puts "abcdabcd".index("cd",3)
puts "*"+"123".ljust(9)+*"
puts "*"+"123".center(9)+"*"
puts "*"+"123".rjust(9)+"*"
puts (eval "2**10")
puts (eval ""мол".size * "око".size')
=begin
s="2*3+4"
puts s
puts "#{s}"
puts eval(s)
puts eval s
puts s.eval # Ошибка! Неправильный вызов метода
print "Введите команды:"
eval gets.chop!
=end
Экземпляр класса Time в языке содержит информацию о дате, времени и временной зоне. Для создания объекта этого класса, содержащего текущие дату и время, используется метод now. Такие объекты могут являться
puts Time.now puts Time.now+60 # добавили 60 секунд к текущему времени puts Time.now-60 # отняли 60 секунд от текущего времени
Отметим некоторые методы этого класса, для вызова которых используется
| Метод | Назначение метода |
|---|---|
sec |
Получить число секунд |
min |
Получить число минут |
hour |
Получить число часов |
mday и day |
Получить день месяца |
mon и month |
Получить номер месяца |
year |
Получить год |
wday |
Получить номер дня недели |
yday |
Получить номер дня в году |
|
Получить информацию о временной зоне |
to_i |
Получить число секунд, прошедших с 1 января 1970 года |
В языке имеется метод , который заставляет программу "заснуть" на число секунд, указанное в качестве аргумента метода. Посмотрите на пример использования методов для работы с объектами класса Time.
puts "До Нового года осталось #{365-Time.now.yday} дней"
puts Time.now
puts "Подождем 10 секунд."
sleep(10)
puts Time.now
#!/usr/bin/env ruby
puts Time.now
puts Time.now+60 # добавили 60 секунд к текущему времени
puts Time.now-60 # отняли 60 секунд от текущего времени
puts "До Нового года осталось #{365-Time.now.yday} дней"
puts Time.now
puts "Подождем 10 секунд."
sleep(10)
puts Time.now
Мы уже видели, как осуществляется вывод информации в . Познакомимся с другими операторами, обеспечивающими печать данных. Создайте файл print.rb, включите в него следующий текст и посмотрите на результат выполнения полученной программы.
puts "puts всегда завершается переходом к новой строке." print "А оператор print не делает этого по умолчанию, " print "как вы видите в этом примере.\n" print "print может быть вызван сразу ", "с несколькими аргументами.\n"
Оператор p подобен оператору puts и отображает объекты в виде, понятном человеку. Не следует использовать его для вывода русских букв - он печатает их ASCII-коды.
p Time.now, Time.now+3600
Значительно большими возможностями обладает оператор printf, обеспечивающий форматный вывод. Сразу после имени оператора указывается строка формата, содержащая как обычные символы, выводимые на печать, так и спецификации преобразования, которые вызывают преобразование и печать остальных аргументов в том порядке, в каком они перечислены. Каждая
- указывает на то, что преобразованный аргумент должен быть прижат к левому
+ предписывает всегда печатать число со знаком;
0 - указывает, что числа должны дополняться ведущими нулями до всей ширины поля;
пробел - указывает, что числа должны дополняться ведущими нулями до всей ширины поля;
Основные литеры-спецификаторы и разъяснение их смысла приводятся в следующей таблице.
| Литера | Аргумент | Вид печати |
|---|---|---|
| d | Fixnum, |
печать целого числа |
| s | String |
печать строки |
| f | Float |
печать дробного числа |
Включите в файл следующие операторы форматной печати и посмотрите на результаты выполнения:
printf "%8s~~%-8s\n", "abcd", "abcd"
printf "%-8s::%8s\n", "abcd", "abcd"
printf "%06d\n", 2**10; printf "%+6d\n", 2**10
printf "%6d\n", 2**10; printf "%+6d\n", 2.5*1.3
printf "%4.3f\n", 2.5*1.3
printf "-2/7=%+1.6f, -2/6=%2.15f\n",
-2/7.to_f, -2/6.to_f
Обратите внимание, что строка формата заключается в двойные кавычки и может содержать специальные последовательности символов. Одной из них является \n. Эта последовательность дает указание интерпретатору продолжить вывод информации со следующей строки (\t (табуляция), которая передвигает фокус вывода к следующей позиции табуляции.
puts "*\t*\t*\t*\t*" puts " 1234567 1234567 1234567 1234567 "
#!/usr/bin/env ruby
puts "puts всегда завершается переходом к новой строке."
print "А оператор print не делает этого по умолчанию, "
print "как вы видите в этом примере.\n"
print "print может быть вызван сразу ",
"с несколькими аргументами.\n"
p Time.now, Time.now+3600
printf "%8s~~%-8s\n", "abcd", "abcd"
printf "%-8s::%8s\n", "abcd", "abcd"
printf "%06d\n", 2**10; printf "%+6d\n", 2**10
printf "%6d\n", 2**10; printf "%+6d\n", 2.5*1.3
printf "%4.3f\n", 2.5*1.3
printf "-2/7=%+1.6f, -2/6=%2.15f\n",
-2/7.to_f, -2/6.to_f
puts "*\t*\t*\t*\t*"
puts " 1234567 1234567 1234567 1234567 "
В этом курсе мы будем использовать так называемые локальные переменные. Имена локальных переменных начинаются со строчной латинской буквы (от a до z) или символа подчеркивания и продолжаются любой комбинацией латинских букв, цифр и символа подчеркивания. Те же правила распространяются и на имена методов.
В языке имеется небольшое множество зарезервированных слов, которые нельзя использовать в качестве имен переменных и методов.
| Зарезервированные слова | |||||||
|---|---|---|---|---|---|---|---|
__FILE__ |
and |
def |
end |
in |
or |
self |
unless |
__LINE__ |
begin |
defined? |
ensure |
module |
|
super |
until |
BEGIN |
break |
do |
false |
next |
rescue |
then |
when |
END |
case |
else |
for |
|
|
true |
while |
|
class |
elsif |
if |
not |
return |
undef |
|
Кроме локальных переменных поддерживает и другие их виды - глобальные переменные, переменные классов и экземпляров. Принадлежность к каждому из этих видов определяются символом, стоящим перед именем переменной (префиксом): символ $ задает глобальную переменную, @ - @@ -
Локальная переменная создается динамически в момент, когда ей в процессе выполнения программы первый раз присваивается какое-то значение. Оператор присваивания имеет вид знака = (равно), например, a = 1; b = a; name = "Иван". Переменные могут использоваться во всех выражениях аналогично соответствующим объектам:
a = "Привет всем!" puts a
В переменные есть ссылки на объекты. Их следует трактовать как некие метки. Для того чтобы лучше понять эту концепцию проведем следующую аналогию: пусть у вас есть некоторая коробка (объект), вы берете наклейку (переменную), подписываете ее (даете имя переменной), после чего наклеиваете ее на коробку (объект). В переменные не имеют типа, так как они есть всего лишь ссылки на объекты произвольных типов. Проиллюстрируем сказанное следующим примером:
a = "one" puts a puts a.type a = 23 puts a puts a.type
Обратите внимание на метод type, возвращающий тип объекта. При первом использовании переменная a есть ссылка на объект класса String, затем мы присваиваем этой переменной другое значение (наклеиваем ту же этикетку на новый объект), после чего она становится ссылкой на экземпляр класса Fixnum.
поддерживает также и присваивание с операцией. Результат выполнения следующих двух операторов одинаковы:
a+=12 a=a+12
Общая форма подобных операторов выглядит так:
выражение1 операция= выражение2
что соответствует оператору
выражение1 = выражение1 операция выражение2
Между знаком операции и символом равенства не должно быть пробела. Такой синтаксис допустим для следующих операций:
+, -, *, /, %, **, , |, ^, <<, >>, , |
Когда требуется присвоить значение нескольким переменным, можно использовать множественное присваивание:
a, b, c = 1, 2, 3
Пример
Пусть требуется обменять значения двух переменных, не вводя вспомогательную переменную. С использованием множественного присваивания это можно записать так:
a, b = b, a
Данный пример объясняет, почему эту форму оператора присваивания называют также "параллельным присваиванием".
Мы уже упоминали одну из замечательных возможностей - подстановку. Она позволяет выполнять разнообразные трюки со строкой, заключенной в двойные кавычки, например,
magic = 42
puts "Секретное число равно #{magic - 3}!!"
Когда находит конструкцию #{выражение} внутри строки, заключенной в двойные кавычки, то заменяет эту ее часть на результат вычисления выражения.
Пример
Дано трехзначное число. Напечатать число, получающееся при перестановке цифр десятков и единиц заданного числа.
number = 342
puts "Исходное число равно #{number}"
n1 = number/100 # число сотен
n2 = (number/10)%10 # число десятков
n3 = number%10 # число единиц
answer = n1*100 + n3*10 + n2
puts "Результат #{answer}"
#!/usr/bin/env ruby
number = 342
puts "Исходное число равно #{number}"
n1 = number/100 # число сотен
n2 = (number/10)%10 # число десятков
n3 = number%10 # число единиц
answer = n1*100 + n3*10 + n2
puts "Результат #{answer}"
Иногда возникает необходимость указать, что тот или иной объект в программе является неизменяемым. Такие объекты называются константами. В имена констант, также как и имена классов, должны начинаться с заглавной буквы (есть определенная традиция задавать имена констант, состоящие из одних заглавных букв). Рассмотрим следующий фрагмент программы:
NUM = 234 p NUM # 234 NUM = "qq" # warning: already initialized constant NUM p NUM # "qq"
Как видим, интерпретатор выдал предупреждение о том, что константа NUM уже была определена ранее. Тем не менее значение константы было изменено.
Задания
Условный оператор, называемый также
поддерживает все конструкции выбора, традиционно используемые в других языках программирования. Но прежде чем говорить о них, остановимся на некоторых особенностях при вычислении истинности отдельных выражений.
Следует запомнить, что только false и трактуются как ложные в логическом контексте. Все остальное, что вычисляется, является истинным. Для того чтобы лучше понять смысл данного высказывания, выполните следующую программу:
if 23 puts "23 есть ИСТИНА" end if "qq" puts "И любая строка есть ИСТИНА" end
поддерживает все стандартные defined?. Для конъюнкции двух логических выражений используются and и . Их результат будет истинен только если инстинны оба операнда. Эти функции вычисляют второй операнд лишь тогда, когда первый истинен, а отличаются они только своим приоритетом (приоритет оператора and меньше ).
В свою очередь результатом выполнения операторов or и || (дизъюнкции) будет истина, если хотя бы один из операндов истинен. Они вычисляют второй оператор только в том случае, если первый окажется ложным. Как и в случае с оператором and, оператор or имеет меньший приоритет по сравнению с || (отметим, что оператор and имеет приоритет, равный приоритету оператора or, в то время как приоритет выше, чем у || ).
Операторы ! возвращают значение, противоположное своему аргументу (если аргумент истинен, то результат ложен, и наоборот). Между собой отличаются только приоритетом.
Оператор defined? возвращает , если его аргумент не определен, и его описание в противном случае. Ниже приведены примеры применения этого оператора.
puts defined? 1 puts defined? dummy puts defined? printf puts defined? (c, d = 1, 2) puts defined? 42.abs
В логических выражениях допускаются также следующие операции сравнения: == <, <=, >=, > !=.
Синтаксис условного оператора if в языке аналогичен его синтаксису в большинстве других языков программирования.
# Определение метода оценки величины выражения
def howBigPrint(i)
if i < 10
puts "Число #{i} меньше 10"
elsif i < 20
puts "Число #{i} между 10 и 20"
elsif i < 30
puts "Число #{i} между 20 и 30"
else
puts "Число #{i} больше или равно 30"
end
end
# Использование метода ...
howBigPrint(7); howBigPrint(15)
howBigPrint(23); howBigPrint(105)
Общая форма оператора if такова:
if <логическое_выражение> [then]
тело_оператора
elsif <логическое_выражение> [then]
тело_оператора
...
else
тело_оператора
end
Здесь <логическое_выражение> ; может быть любым фрагментом кода на языке , результатом вычисления которого является логическая величина (с учетом сказанного выше). Слово then отделяет if, есть результат последнего вычисленного выражения. Изменим наш пример, чтобы продемонстрировать сказанное:
# Метод оценки величины выражения # теперь возвращает строку def howBig(i) if i < 10 then "Аргумент меньше 10" elsif i < 20 then "Аргумент между 10 и 20" elsif i < 30 then "Аргумент между 20 и 30" else "Аргумент больше или равно 30" end end # Использование метода ... puts howBig(7); puts howBig(15) puts howBig(23); puts howBig(105)
Для выполнения одного варианта из двух возможных используется следующая форма оператора if:
if <логическое_выражение> [then]
тело_оператора
else
тело_оператора
end
В следующем фрагменте сравнивается значение переменной i с числом 100.
if i < 100 puts "Мало" else puts "Значительно больше" end
Оператор if может использоваться в правой части оператора присваивания, например,
str = if i < 100 then "Мало" else "Побольше" end puts str
Для аналогичных целей используется и тернарный оператор
логическое_выражение ? выражение1 : выражение2
Если логическое_выражение истинно, то выполняется выражение1, иначе - выражение2. Посмотрите на примеры использования этого оператора:
i = 23
i < 100 ? puts("Мало") : puts("Значительно больше")
i = 1234
s = i < 100 ? "Мало" : "Значительно больше"
puts s
Как видите, этот оператор особенно удобен, если требуется переменной присвоить одно из двух значений.
Пример
Напишите программу, определяющую четность введенного числа.
print "Введите целое число: "
a = gets.to_i
str = a%2 == 0 ? "четно" : "нечетно"
puts "Число #{a} " + str
Если требуется выполнить последовательность операторов только в том случае, когда выполнено некоторое условие, то используется форма
if <логическое_выражение> [then]
тело_оператора
end
Для этих же целей можно использовать и модифицированный оператор if:
выражение if <логическое_выражение>
Ниже приводятся две версии фрагмента программы, использующей оператор if:
if radiation > 3000 puts "Радиационная опасность!" end # то же самое puts "Радиационная опасность!" if radiation > 3000
Пример
Напишите программу, определяющую, является ли введенная фраза палиндромом (перевертышем).
puts "Введите фразу:"
a = gets.chop!.delete(' ') # удалили все пробелы
a = a.tr('A-Z,А-Я', 'a-z,а-я') # замена букв на прописные
str = "палиндром"
str = "не " + str if a != a.reverse # проверка
puts "Введенная фраза - " + str + "."
При выборе из большего числа альтернатив удобнее использовать оператор case. Ниже приведен его общий вид (части, заключенные в квадратные скобки, могут быть опущены).
case <выражение>
when <тест1> [then]
...
when <тест2> [then]
...
when <тестN> [then]
...
[else
... ]
end
В операторе case <выражение> последовательно сравнивается с выражениями <тест> до тех пор пока, оно не совпадет с одним из них (сравнение производится при помощи операции == ), после чего выполняется соответствующий фрагмент кода. Оператор case возвращает значение последнего вычисленного выражения, или , если такого не было.
s = case i
when 0 .. 9
"однозначное"
when 10 .. 99
"двузначное"
when 100 .. 999
"трехзначное"
else
"огромное"
end
puts "Введено #{s} число."
Здесь мы снова воспользовались оператором .. (двоеточие), который возвращает список целых чисел, заключенных между левым и правым операндами (включая их самих).
#!/usr/bin/env ruby
if 23
puts "23 есть ИСТИНА"
end
if "qq"
puts "И любая строка есть ИСТИНА"
end
puts
# Определение метода оценки величины выражения
def howBigPrint(i)
if i < 10
puts "Число #{i} меньше 10"
elsif i < 20
puts "Число #{i} между 10 и 20"
elsif i < 30
puts "Число #{i} между 20 и 30"
else
puts "Число #{i} больше или равно 30"
end
end
# Использование метода ...
howBigPrint(7); howBigPrint(15)
howBigPrint(23); howBigPrint(105)
puts
# Метод оценки величины выражения
# теперь возвращает строку
def howBig(i)
if i < 10 then "Аргумент меньше 10"
elsif i < 20 then "Аргумент между 10 и 20"
elsif i < 30 then "Аргумент между 20 и 30"
else "Аргумент больше или равно 30"
end
end
# Использование метода ...
puts howBig(7); puts howBig(15)
puts howBig(23); puts howBig(105)
puts
i = 23
i < 100 ? puts("Мало") : puts("Значительно больше")
i = 1234
s = i < 100 ? "Мало" : "Значительно больше"
puts s
puts "Перевертыш"
puts "Введите фразу:"
a = gets.chop!.delete(' ') # удалили все пробелы
a = a.tr('A-Z,А-Я', 'a-z,а-я') # замена букв на прописные
str = "палиндром"
str = "не " + str if a != a.reverse # проверка
puts "Введенная фраза - " + str + "."
print "Введите неотрицательное целое число: "
i= gets.to_i
s = case i
when 0 .. 9
"однозначное"
when 10 .. 99
"двузначное"
when 100 .. 999
"трехзначное"
else
"огромное"
end
puts "Введено #{s} число."
Задания
Для задания повторяющихся действий в большинстве языков программирования используются имеется два таких оператора - while и until, а также большое количество
Оператор while выполняет операторы, составляющее его тело, ноль или более раз, до тех пор, пока истинно его условие, задаваемое некоторым логическим выражением. Его общий вид таков:
while <выражение> [do]
...
тело цикла
...
end
Другим оператором цикла является until, выполняющийся до тех пор, пока его условие ложно:
until <выражение> [do]
...
тело цикла
...
end
Пример
Рассмотрим программу, печатающую числа от 1 до 5. Сначала используем оператор while, затем until. Обратите внимание, что условие окончания одного оператора цикла является отрицанием условия другого оператора.
i=1 while i <= 5 puts i; i += 1 end # еще раз i=1 until i > 5 puts i; i += 1 end
Кроме этих двух операторов цикла в имеется большое число, так называемых,
3.times do print "Ау! " end
использует times. Цикл, заданный таким образом, выполнится ровно три раза.
В случае, когда нужно выполнить некоторые действия, зависящие от изменяемой величины, несколько раз, можно использовать
0.upto(9) do |x| print x, " " end
будет напечатано 0 1 2 3 4 5 6 7 8 9.
Повтор от 0 до 12 с шагом 3 можно записать при помощи step:
0.step(12, 3) {|x| print x, " " } # 0 3 6 9 12
При работе с массивами удобно использовать each:
[1, 1, 2, 3].each {|k| print k, " " } # 1 1 2 3
for in очень похож на each, например, вывод, полученный в результате выполнения следующих двух конструкций одинаков.
for i in ["one", "two", "three"]
print i, " "
end
# то же самое
["one", "two", "three"].each{ |i| print i, " "}
for обычно используют там же, где и each - при работе с массивами и диапазонами. Общий вид оператора for таков:
for <переменная> in <выражение> [do] тело_итератора end
Пример
Перепишем программу печати чисел от 1 до 5 с использованием оператора for:
for i in 1 .. 5 puts i end
В этом примере мы снова воспользовались оператором задания диапазона .. (двоеточие), который позволил нам создать список чисел от 1 до 5 (включительно). Похожий оператор ... (троеточие) при создании диапазона не включает в него правый операнд. Фрагмент программы, расположенный ниже, эквивалентен предыдущему.
for i in 1 ... 6 puts i end
Пример
Рассмотрим несколько вариантов программы, вычисляющей факториал введенного числа.
whileprint "Введите целое положительное число: "
str = gets.chop! # ввели строку
num = str.to_i # преобразовали в число
if num > 0
i = 1
fact = 1
while i <= num
fact *= i
i += 1
end
puts "Факториал числа #{num} равен #{fact}"
else
puts "Вы ввели неположительное число"
end
forprint "Введите целое положительное число: "
num = gets.to_i # строку сразу преобразовали в число
if num > 0
fact = 1
for i in 1 .. num
fact *= i
end
puts "Факториал числа #{num} равен #{fact}"
else
puts "Вы ввели неположительное число"
end
def fact(n)
f = 1
1.step(n,1) {|k| f *= k}
return f
end
print "Введите целое положительное число: "
if (num = gets.to_i) > 0
print "#{num}! = #{fact(num)}\n"
else
puts "Факториал числа #{num} не определен\n"
end
В последнем примере мы оформили вычисление факториала в виде функции и использовали step, который обеспечил изменение переменной k с шагом 1.
Пример
Приведенная ниже программа запрашивает целое положительное число и определяет количество цифр в нем (обратите внимание на множественное присваивание).
print "Введите целое положительное число: "
a, k = gets.to_i, 0
while a>0
a /= 10; k += 1 # отбросили последнюю цифру
end
print "Количество цифр в введенном числе равно #{k}.\n"
Пример
Пусть нужно ввести с клавиатуры массив чисел и напечатать сумму всех элементов. Приведем несколько решений этой задачи.
print "Введите число элементов массива: "
sn = gets.chop!; n = sn.to_i
b = Array.new(n) # создали массив из n элементов
s = 0 # обнулили сумму
for i in 0 .. n - 1
print "Введите #{i+1}-й элемент массива: "
b[i] = gets.chop!.to_f; s = s + b[i]
end
print "Сумма всех элементов массива равна ", s, "\n"
Встроенный метод split разделяет строку на элементы массива, аргументом этого метода является разделитель (если разделителем является пробел, то можно вызвать этот метод без аргумента). Таким образом, если бы мы договорились разделять числа, например, точкой с запятой, то вызов метода выглядел бы так: a.split(';').
Для определения длины массива мы применили метод length (можно заменить на эквивалентный ему метод size ).
puts "Введите массив чисел (разделяя их пробелами):"
a = gets.chop!
b = a.split # разбили строку на отдельные числа
s = 0
for i in 0 .. b.length - 1
s += b[i].to_f
end
puts "Сумма всех элементов массива равна #{s}"
В break, , next и , которые изменяют обычный порядок выполнения циклов. Их действие описано в следующей таблице.
break |
Немедленно прекращает выполнение |
|
Повторяет тело цикла с начала, не пересчитывая условие выполнения цикла (не переходя к следующему элементу в случае |
next |
Пропускает часть тела цикла, следующую за ним, и переходит к следующей итерации |
|
Начинает выполнение цикла с самого начала |
Рассмотрим на примере for действие указанных конструкций.
for i in 1 .. 5 print i break if i == 3 print "*" end |
Результат: 1*2*3 |
for i in 1 .. 5 print i redo if i == 3 print "*" end |
Результат: 1*2*33333 ... выполнение цикла не прекращается |
for i in 1 .. 5 print i next if i == 3 print "*" end |
Результат: 1*2*34*5* |
for i in 1 .. 5 print i retry if i == 3 print "*" end |
Результат: 1*2*31*2*31*2*... выполнение цикла не прекращается |
Пример
Следующая программа начинает повторение цикла сначала, если при вводе указать символ y.
for i in 1 .. 5
print "Now at #{i}. Restart?(y/n) "
retry if gets.chop == "y"
end
Вот один из возможных вариантов выполнения этой программы:
Now at 1. Restart?(y/n) n
Now at 2. Restart?(y/n) y
Now at 1. Restart?(y/n) n
...
#!/usr/bin/env ruby
i=1
while i <= 5
puts i; i += 1
end
puts
# еще раз
i=1
until i > 5
puts i; i += 1
end
puts
3.times do
print "Ау! "
end
puts
0.upto(9) do |x| print x, " " end
puts
0.step(12, 3) {|x| print x, " " } # 0 3 6 9 12
puts
[1, 1, 2, 3].each {|k| print k, " " } # 1 1 2 3
puts
for i in ["one", "two", "three"]
print i, " "
end
puts
# то же самое
["one", "two", "three"].each{ |i| print i, " "}
puts
for i in 1..5
puts i
end
puts
for i in 1 ... 6
puts i
end
puts
print "Введите целое положительное число: "
str = gets.chop! # ввели строку
num = str.to_i # преобразовали в число
if num > 0
i = 1
fact = 1
while i <= num
fact *= i
i += 1
end
puts "Факториал числа #{num} равен #{fact}"
else
puts "Вы ввели неположительное число"
end
puts
print "Введите целое положительное число: "
num = gets.to_i # строку сразу преобразовали в число
if num > 0
fact = 1
for i in 1 .. num
fact *= i
end
puts "Факториал числа #{num} равен #{fact}"
else
puts "Вы ввели неположительное число"
end
puts
def fact(n)
f = 1
1.step(n,1) {|k| f *= k}
return f
end
print "Введите целое положительное число: "
if (num = gets.to_i) > 0
print "#{num}! = #{fact(num)}\n"
else
puts "Факториал числа #{num} не определен\n"
end
puts
print "Введите целое положительное число: "
a, k = gets.to_i, 0
while a>0
a /= 10; k += 1 # отбросили последнюю цифру
end
print "Количество цифр в введенном числе равно #{k}.\n"
puts
print "Введите число элементов массива: "
sn = gets.chop!; n = sn.to_i
b = Array.new(n) # создали массив из n элементов
s = 0 # обнулили сумму
for i in 0 .. n - 1
print "Введите #{i+1}-й элемент массива: "
b[i] = gets.chop!.to_f; s = s + b[i]
end
print "Сумма всех элементов массива равна ", s, "\n"
puts
puts "Введите массив чисел (разделяя их пробелами):"
a = gets.chop!
b = a.split # разбили строку на отдельные числа
s = 0
for i in 0 .. b.length - 1
s += b[i].to_f
end
puts "Сумма всех элементов массива равна #{s}"
puts
=begin
for i in 1 .. 5
print i
break if i == 3
print "*"
end
puts
for i in 1 .. 5
print i
redo if i == 3
print "*"
end
puts
for i in 1 .. 5
print i
next if i == 3
print "*"
end
puts
for i in 1 .. 5
print i
retry if i == 3
print "*"
end
puts
=end
for i in 1 .. 5
print "Now at #{i}. Restart?(y/n) "
retry if gets.chop == "y"
end
puts
Задания
При рассмотрении задач, связанных с обработкой целых чисел, часто приходится сталкиваться с понятием НОД - наибольшего общего делителя чисел. Известно много алгоритмов вычисления НОД, мы рассмотрим лишь два из них.
Задача
Напишите программу, вычисляющую НОД(a,b) - наибольший общий делитель двух введенных с клавиатуры неотрицательных целых чисел a и b, не равных нулю одновременно.
Вариант 1
Определим максимум из этих чисел и, последовательно уменьшая его, будем искать число, на которое делятся и a и b. Программа обязательно завершит свою работу, так как в самом неудачном случае, когда эти числа взаимно просты, k станет равно 1, а на 1 делятся все числа, следовательно выполнение цикла прекратится.
print "Введите первое число: "; a = gets.to_i
print "Введите второе число: "; b = gets.to_i
k = a >= b ? a : b # теперь k - максимум
until (a % k == 0) and (b % k == 0)
k -= 1
end
print "НОД(#{a},#{b}) = #{k}\n"
Вариант 2 - алгоритм Евклида
НОД: для всех a и b, больших или равных 0, выполнены равенства
НОД(a, b) = НОД(a - b, b)= НОД(a, b - a); НОД(a, 0) = НОД(0, a) = a
Ниже приведен текст программы, реализующей
print "Введите первое число: "; a = gets.to_i
print "Введите второе число: "; b = gets.to_i
m, n = a, b
while !((m == 0) || (n == 0))
if m >= n
m = m - n
else
n = n - m
end
end
k = m == 0 ? n : m
print "НОД(#{a},#{b}) = #{k}\n"
#!/usr/bin/env ruby
# 1
print "Введите первое число: "; a = gets.to_i
print "Введите второе число: "; b = gets.to_i
k = a >= b ? a : b # теперь k - максимум
until (a%k == 0) (b%k == 0)
k-=1
end
print "НОД(#{a},#{b}) = #{k}\n"
#2 Алгоритм Евклида
print "Введите первое число: "; a = gets.to_i
print "Введите второе число: "; b = gets.to_i
m, n = a, b
while !((m == 0) || (n == 0))
if m >= n
m = m - n
else
n = n - m
end
end
k = m == 0 ? n : m
print "НОД(#{a},#{b}) = #{k}\n"
Как и другие языки программирования, позволяет передавать данные в программу, использую
=begin
Напишите программу подсчета суммы всех нечетных
чисел, заключенных в интервале от K до L,
где K и L - аргументы командной строки
=end
if ARGV.length < 2
puts "Слишком мало входных данных"
exit(1)
end
s = 0
k = ARGV[0].to_i
l = ARGV[1].to_i
for i in k .. l
s += i if i%2 != 0
end
puts "Сумма нечетных чисел в интервале " +
"от #{k} до #{l} равна #{s}"
Для экстренного прерывания процесса выполнения программы здесь использован встроенный метод exit.
#!/usr/bin/env ruby
=begin
Напишите программу подсчета суммы всех нечетных
чисел, заключенных в интервале от K до L,
где K и L - аргументы командной строки
=end
if ARGV.length < 2
puts "Слишком мало входных данных"
exit(1)
end
s = 0
k = ARGV[0].to_i
l = ARGV[1].to_i
for i in k .. l
s += i if i%2 != 0
end
puts "Сумма нечетных чисел в интервале " +
"от #{k} до #{l} равна #{s}"
Задача
n, и печатающую n -е
Вариант 1 - рекурсивная функция
В следующей программе первый элемент массива ARGV преобразовывается в целое число и передается в качестве аргумента рекурсивной функции .
def fib(n)
if n<2
n
else
fib(n-2)+fib(n-1)
end
end
n = ARGV[0].to_i
puts "#{n}-е число Фибоначчи равно #{fib(n)}"
Для вычисления с помощью этой программы, размещенной в файлe , например, 30-го
Данная программа использует двойную рекурсию, поэтому процесс вычисления займет достаточно заметное время.
#!/usr/bin/env ruby
=begin
Напишите программу, получающую с качестве аргумента командной строки целое
число n, и печатающую n-е число Фибоначчи.
=end
def fib(n)
if n<2
n
else
fib(n-2)+fib(n-1)
end
end
t1 = Time.now
n = ARGV[0].to_i
puts "#{n}-е число Фибоначчи равно #{fib(n)}"
t2 = Time.now
puts "Время расчета около #{(t2-t1).round} сек."
Вариант 2 - использование массива
В следующем варианте решения этой задачи каждое
n =ARGV[0].to_i
f= [0, 1]
fib = case n
when 0
f[0]
when 1
f[1]
else
for i in 2 .. n
f[i] = f[i-1] + f[i-2]
end
f[n]
end
puts "#{n}-е число Фибоначчи равно #{fib}"
#!/usr/bin/env ruby
=begin
Напишите программу, получающую с качестве аргумента командной строки целое
число n, и печатающую n-е число Фибоначчи.
=end
t1 = Time.now
n =ARGV[0].to_i
f= [0, 1]
fib = case n
when 0
f[0]
when 1
f[1]
else
for i in 2 .. n
f[i] = f[i-1] + f[i-2]
end
f[n]
end
puts "#{n}-е число Фибоначчи равно #{fib}"
t2 = Time.now
puts "Время расчета около #{(t2-t1).round} сек."
a = fib.to_s.split('")
puts "Количество цифр в #{n}-м числе Фибоначчи равно #{a.size}."
Однако и у этой программы есть существенный недостаток - она хранит все промежуточные результаты, в то время как каждое предыдущее значение используется лишь дважды. Изменим программу так, чтобы она использовала только три переменных: вычисляемое значение и два предшествующих ему:
n =ARGV[0].to_i
if n > 1
prev, beforePrev = 1, 0
for i in 2 .. n
fib = prev + beforePrev
prev, beforePrev = fib, prev
end
else
fib = n
end
puts "#{n}-е число Фибоначчи равно #{fib}"
#!/usr/bin/env ruby
=begin
Напишите программу, получающую с качестве аргумента командной строки целое
число n, и печатающую n-е число Фибоначчи.
=end
t1 = Time.now
n =ARGV[0].to_i
if n > 1
prev, beforePrev = 1, 0
for i in 2 .. n
fib = prev + beforePrev
prev, beforePrev = fib, prev
end
else
fib = n
end
puts "#{n}-е число Фибоначчи равно #{fib}"
t2 = Time.now
puts "Время расчета около #{(t2-t1).round} сек."
a = fib.to_s.split("')
puts "Количество цифр в #{n}-м числе Фибоначчи равно #{a.size}."
Включите в рассмотренные программы операторы, информирующие о продолжительности вычислений, и сравните их эффективность. Для этого найдите с помощью первой из рассмотренных программ 30, 31 и 32
Ввод данных с клавиатуры в процессе выполнения программы удобен только в случае небольших объемов. В других ситуациях необходимо чтение информации из заранее подготовленного файла.
Для работы с файлом при помощи метода new создается экземпляр класса File. Обязательным аргументом этого метода является строка, содержащая имя файла, например,
f = File.new("myfile.txt")
Другим (необязательным) аргументом является задание режима работы с файлом. По умолчанию этот параметр имеет значение " r ", что соответствует режиму "только чтение". Если требуется открыть файл с возможностью записи в него, то следует указать параметр " w ", например,
f1 = File.new("newfile.txt", "w")
Метод readlines считывает весь файл, создает массив и размещает каждую прочитанную строку в отдельном элементе массива При дальнейшей обработке полученного массива удобно использовать метод each.
Для вывода строки в выходной поток (в файл или на экран монитора) используется метод write, например,
a = "Hello, world!" f1.write(a)
Задача
Имеется текстовый файл fio.txt, содержащий список фамилий, имен и отчеств учащихся. При этом каждая строка файла содержит данные только об одном человеке, например,
Петров Сергей Васильевич Сидорова Ольга Петровна Иванова Марья Даниловна
Напишите программу, которая читает информацию из файла и
Решение 1
f = File.new("fio.txt")
n = 1
student = f.readlines
student.each{ |i|
print n, ". ", i
n += 1
}
Отметим, что программа должна располагаться в той же директории, что и файл с данными. В противном случае необходимо указывать полный путь к этому файлу.
Для получения инициалов каждую строку полученного массива преобразуем в массив, элементами которого будут фамилия, имя и отчество. Затем фамилию печатаем полностью, а вместо следующих элементов массива - только их первый символ. Для преобразования строки в массив используется ранее рассмотренная функция split. Напомним, что параметром по умолчанию этой функции является пробел.
Решение 2
f = File.new("fio.txt")
n = 1
student = f.readlines
student.each{ |i|
i.chop!
fio = i.split
print n, ". ", fio[0], " ", fio[0][0].chr, ". ",
fio[1][0].chr, ".\n"
n += 1
}
#!/usr/bin/env ruby
# 1
f = File.new("fio.txt")
n = 1
student = f.readlines
student.each{ |i|
print n, ". ", i
n += 1
}
puts
# 2
f = File.new("fio.txt")
n = 1
student = f.readlines
student.each{ |i|
i.chop!
fio = i.split
print n, ". ", fio[0], " ", fio[0][0].chr, ". ",
fio[1][0].chr, ".\n"
n += 1
}
Моделирование случайных процессов на компьютере - достаточно распространенное явление. Как и в большинстве языков программирования в есть методы, генерирующие, так называемые, "псевдослучайные" числа (их нельзя считать чисто случайными, ведь создаются они при помощи некоторого алгоритма).
Для получения случайного целого числа в диапазоне от 0 до n используется метод rand(n). Случайное число в диапазоне от 0 до 1 получается с помощью этого же метода без указания параметра.
a1 = (1..10).collect { |j| rand(100)}
a2 = (1..4).collect { |j| rand()}
p a1, a2
Ниже представлен возможный вывод:
[64, 17, 21, 95, 58, 24, 29, 60, 47, 63] [0.2009671596, 0.8890923676, 0.3349569312, 0.8719313448]
Одним из применений случайных чисел является
Задача
Напишите программу, вычисляющее приближенное значение числа PI при помощи
Решение
Число PI равно отношению площади круга к квадрату его радиуса. Впишем круг единичного радиуса с центром в начале координат в квадрат и методом Монте-Карло найдем его площадь. Координаты случайных точек, бросаемых в круг, принадлежат интервалу (-1; 1). Величина, случайно распределенная в этом интервале, задается выражением 2*rand() - 1.
Для контроля за временем добавим два экземпляра класса Time: время начала и окончания расчета. Программа, реализующая этот алгоритм, представлена ниже. В ней используется оператор , чтобы количество точек можно было задавать в виде арифметического выражения.
puts "Введите количество точек:"
n1, n, t1 = 0, eval(gets.chop), Time.now
for i in 1 .. n
x = 2*rand() - 1
y = 2*rand() - 1
# проверяем попадание внутрь круга
n1 += 1 if (x**2 + y**2) < 1
end
puts "PI=#{4.0*n1/n}"
t2 = Time.now
puts "Число точек #{n}, время расчета " +
"около #{(t2 - t1).round} сек."
Возможные результаты работы программы будут похожи (случайность!) на следующие:
Введите количество точек: 10**5 PI=3.14028 Число точек 100000, время расчета около 2 сек. Введите количество точек: 10**6 PI=3.142204 Число точек 1000000, время расчета около 20 сек.
#!/usr/bin/env ruby
# Метод Монте-Карло
puts "Введите количество точек:"
n1, n, t1 = 0, eval(gets.chop), Time.now
for i in 1 .. n
x = 2*rand() - 1
y = 2*rand() - 1
# проверяем попадание внутрь круга
n1 += 1 if (x**2 + y**2) < 1
end
puts "PI=#{4.0*n1/n}"
t2 = Time.now
puts "Число точек #{n}, время расчета " +
"около #{(t2 - t1).round} сек."
Пример программы на языке , демонстрирующий работу с классами, уже приводился в глава 9. Следующий пример демонстрирует использование так называемой переменной класса, которая не принадлежит ни одному из экземпляров данного класса, а используется для хранения информации, относящейся ко всему классу в целом.
Задача
Напишите программу, описывающую класс автомобиль и два его дочерних класса: спортивные машины и машины для семейного использования. В программе нужно предусмотреть возможность печати общего числа созданных автомобилей.
Решение
Поместите в файл с именем car.rb следующую программу:
class Car
@@NUM_CARS = 0
def initialize
@@NUM_CARS = @@NUM_CARS + 1
puts @@NUM_CARS
end
end
class SportsCar < Car
end
class FamilyCar < Car
end
a = Car.new
b = SportsCar.new
c = FamilyCar.new
Для запуска программы выполните команду . При создании каждого автомобиля, независимо от того, к какому (родительскому или дочернему) классу он принадлежит, значение переменной класса @@NUM_CARS увеличивается на единицу.
#!/usr/bin/env ruby
class Car
@@NUM_CARS = 0
def initialize
@@NUM_CARS = @@NUM_CARS + 1
puts @@NUM_CARS
end
end
class SportsCar < Car
end
class FamilyCar < Car
end
a = Car.new
b = SportsCar.new
c = FamilyCar.new
Задания
НОД.y=sin(x) и y=0 ( x изменяется в интервале от 0 до PI ).Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.