Некоммерческая организация «Ассоциация московских вузов» Государственное образовательное учреждение высшего профессионального образования Московский государственный индустриальный университет ГОУ ВПО МГИУ Научно-образовательный материал «Программисты пишут на Ruby» Состав научно-образовательного коллектива: Курасов Ю.В., ведущий инженер Виноградова Л.А., ведущий инженер Москва 2010 г. Программисты пишут на Ruby Начнем с примеров, показывающих, что знание библиотек ввода/вывода языков C и C++ пригодится и в Ruby. printf "Число: %5.2f; Строка: %s", 1.23, "Привет!" endl = "\n"; $stdout << 17 << " красных шариков" << endl line = gets; print line Специальная глобальная переменная $_ всегда содержит результат последней операции чтения. Она же используется как аргумент по умолчанию во многих конструкциях. Следующая программа, например, печатает все строки из входного потока, которые содержат слово Ruby. while gets # присваивание очередной строки переменной $_ if /Ruby/ print # сопоставление ее с образцом Ruby # печать $_ end end Ruby-стиль, однако, рекомендует использовать итератор each: ARGF.each { |line| print line if line =~ /Ruby/ } ARGF в Ruby -- это объект, который представляет собой конкатенацию содержимого всех файлов, имена которых заданы в командной строке, либо просто стандартный поток ввода (в случае отсутствия аргументов). Вот как выглядит на Ruby программа вычисления факториала числа, указываемого в качестве аргумента командной строки: def fact(n) return 1 if n == 0 f = 1 while n>0 f *= n n -= 1 end return f end print fact(ARGV[0].to_i), "\n" Для вычисления с помощью этой программы, размещенной в файле fact.rb, значения 100! достаточно выполнить команду ruby fact.rb 100. Три программы, приведенные рекурсивно ниже, вычисляющие позволяют интерпретаторов Ruby, Python и Perl. # Ruby def fib(n) if n<2 n else fib(n-2)+fib(n-1) end end print fib(30), "\n" # Python def fib(n): if n<2: return n 30-е сравнить число Фибоначчи, производительность else: return fib(n-2)+fib(n-1) print fib(30) # Perl sub fib { my($n)=@_; if ($n<2) { return $n; } else { return fib($n-2)+fib($n-1); } } print fib(30), "\n"; Еще одна классическая задача -- определение с помощью решета Эратосфена списка всех простых чисел, не превосходящих заданного (100 по умолчанию). max = Integer(ARGV.shift || 100) sieve = [] for i in 2 .. max sieve[i] = i end for i in 2 .. Math.sqrt(max) next unless sieve[i] (i*i).step(max, i) do |j| sieve[j] = nil end end puts sieve.compact.join ", " В качестве следующего примера рассмотрим решение на языке Ruby задачи, которая часто предлагается студентам первого курса, изучающим языки C/C++: для заданного текстового файла определить число вхождений в него каждого из встречающихся в нем слов. Использование ассоциативных массивов и ряда стандартных методов работы с файлами и строками позволяет написать чрезвычайно краткую и ясную программу. freq = Hash.new(0) while gets() for word in $_.split(/\W+/) freq[word] += 1 end end for word in freq.keys.sort! print word, " -- ", freq[word], "\n" end Приведем два простых примера использования стандартной библиотеки классов. В результате выполнения первой программы будет найдено, что 7/8+1/8=1, а 7/8*1/8=7/64; вторая из них вычислит (1 + i)64. require "rational" a = Rational(7,8) b = Rational(1,8) print a, "+", b, "=", a+b, "; ", a, "*", b, "=", a*b, "\n" require "complex" a = Complex(1,1); print a**64 Без подробных объяснений приведем две эквивалентные программы, иллюстрирующие переопределение оператора [] для класса SongList. Ассоциативный массив (associative array, hash или dictionary) допускает индексирование произвольными объектами, а не только целыми числами. В данном случае оператор [] позволяет находить нужную песню не только по номеру, но и по ее названию. class SongList def [](key) if key.kind_of?(Integer) return @songs[key] else for i in [email protected] return @songs[i] if key == @songs[i].name end end return nil end end class SongList def [](key) return @songs[key] if key.kind_of?(Integer) return @songs.find { |aSong| aSong.name == key } end end Так как Ruby унаследовал лучшие особенности многих языков, то для выполнения достаточно стандартных действий обычно имеется несколько разных возможностей. Вот 13 (!) различных способов напечатать числа от 0 до 9: i = 0 i = 0 while i < 10 begin print i , ' ' print i , ' ' i += 1 i += 1 end end while i < 10 i = 0 i = 0 until i >= 10 begin print i , ' ' print i, ' ' i += 1 i += 1 end end until i >= 10 for i in [0,1,2,3,4,5,6,7,8,9] for i in (0..9) print i, ' ' print i, ' ' end end for i in (0...10) 10.times do |i| print i, ' ' print i, ' ' end end 0.upto(9) do |i| 9.downto(0) do |i| print i, ' ' print i, ' ' end end (0..9).each do |i| (0...10).each do |i| print i, ' ' end i = 0 loop do print i, ' ' end if i < 10 then print i, ' ' else break end i += 1 end В заключение вопрос для тех, кто не знает языка Ruby: что напечатает следующая программа? print ["L", "R", "H", "T"].collect { |x| x.succ } Если вы сумеете угадать ответ, то это будет лучшим подтверждением тому факту, что Ruby интуитивно ясный язык. Если же не угадаете, то у вас будет еще одна причина изучить его.