Хэл Фултон - Программирование на языке Ruby
puts "Значение определителя равно #{b*b — 4*а*с}."
puts "#{word} это #{word.reverse} наоборот."
Внутри фигурных скобок могут находиться даже полные предложения. При этом возвращается результат вычисления последнего выражения.
str = "Ответ равен #{ def factorial(n)
n==0 ? 1 : n*factorial(n-1)
end
answer = factorial(3) * 7}, естественно."
# Ответ равен 42, естественно.
При интерполяции глобальных переменных, а также переменных класса и экземпляра фигурные скобки можно опускать:
print "$gvar = #$gvar и ivar = #@ivar."
Интерполяция не производится внутри строк, заключенных в одиночные кавычки (поскольку их значение не интерпретируется), но применима к заключенным в двойные кавычки встроенным документам и к регулярным выражениям.
2.22. Отложенная интерполяция
Иногда желательно отложить интерполяцию значений в строку. Идеального способа решить эту задачу не существует, но можно воспользоваться блоком:
str = proc {|x,у,z| "Числа равны #{x}, #{у} и #{z}" }
s1 = str.call(3,4,5) # Числа равны 3, 4 и 5.
s2 = str.call(7,8,9) # Числа равны 7, 8 и 9.
Другое, более громоздкое решение состоит в том, чтобы сохранить строку, заключенную в одиночные кавычки, потом «обернуть» ее двойными кавычками и вычислить:
str = '#{name} - мое имя, а #{nation} - моя родина'
name, nation = "Стивен Дедал", "Ирландия"
s1 = eval('"' + str + '"')
# Стивен Дедал - мое имя, а Ирландия - моя родина.
Можно также передать eval другую функцию привязки:
bind = proc do
name,nation = "Гулливер Фойл", "Земля"
binding
end.call # Надуманный пример; возвращает привязанный контекст блока
s2 = eval('"' + str + '"',bind)
# Гулливер Фойл - мое имя, а Земля - моя родина.
У техники работы с eval есть свои «причуды». Например, будьте осторожны, вставляя управляющие последовательности, скажем n.
2.23. Разбор данных, разделенных запятыми
Данные, разделенные запятыми, часто встречаются при программировании. Это в некотором роде «наибольший общий делитель» всех форматов обмена данными. Например, так передаются данные между несовместимыми СУБД или приложениями, которые не поддерживают никакого другого общего формата.
Будем предполагать, что данные представляют собой строки и числа, а все строки заключены в кавычки. Еще предположим, что все символы должным образом экранированы (например, запятые и кавычки внутри строки).
Задача оказывается простой, поскольку такой формат данных подозрительно напоминает встроенные в Ruby массивы данных разных типов. Достаточно заключить все выражение в квадратные скобки, чтобы получить массив.
string = gets.chop!
#Предположим, что прочитана такая строка:
#"Doe, John", 35, 225, "5'10"", "555-0123"
data = eval("[" + string + "]") # Преобразовать в массив.
data.each {|x| puts "Значение = #{x}"}
Этот код выводит такой результат:
Значение = Doe, John
Значение =35
Значение =225
Значение = 5' 10"
Значение = 555-0123
Более общее решение дает стандартная библиотека CSV. Есть также усовершенствованный инструмент под названием FasterCSV. Поищите его в сети, он не входит в стандартный дистрибутив Ruby.
2.24. Преобразование строки в число (десятичное или иное)
Есть два основных способа преобразовать строку в число: методы Integer и Float модуля Kernel и методы to_i и to_f класса String. (Имена, начинающиеся с прописной буквы, например Integer, обычно резервируются для специальных функций преобразования.)
Простой случай тривиален, следующие два предложения эквивалентны:
x = "123".to_i # 123
y = Integer("123") # 123
Но если в строке хранится не число, то поведение этих методов различается:
x = junk".to_i # Молча возвращает 0.
y = Integer("junk") # Ошибка.
Метод to_i прекращает преобразование, как только встречает первый символ, не являющийся цифрой, а метод Integer в этом случае возбуждает исключение:
x = "123junk".to_i # 123
y = Integer("123junk") # Ошибка.
Оба метода допускают наличие пропусков в начале и в конце строки:
x = " 123 ".to_i # 123
y = Integer(" 123 ") # 123
Преобразование строки в число с плавающей точкой работает аналогично:
x = "3.1416".to_f # 3.1416
y = Float("2.718") # 2.718
Оба метода понимают научную нотацию:
x = Float("6.02е23") # 6.02е23
y = "2.9979246е5".to_f # 299792.46
Методы to_i и Integer также по-разному относятся к системе счисления. По умолчанию, естественно, подразумевается система по основанию 10, но другие тоже допускаются (это справедливо и для чисел с плавающей точкой).
Говоря о преобразовании из одной системы счисления в другую, мы всегда имеем в виду строки. Ведь целое число неизменно хранится в двоичном виде.
Следовательно, преобразование системы счисления — это всегда преобразование одной строки в другую. Здесь мы рассмотрим преобразование из строки (обратное преобразование рассматривается в разделах 5.18 и 5.5).
Числу в тексте программы может предшествовать префикс, обозначающий основание системы счисления. Префикс 0b обозначает двоичное число, 0 — восьмеричное, а 0x — шестнадцатеричное.
Метод Integer такие префиксы понимает, а метод to_i — нет:
x = Integer("0b111") # Двоичное - возвращает 7.
y = Integer("0111") # Восьмеричное - возвращает 73.
z = Integer("0x111") # Шестнадцатеричное - возвращает 291.
x = "0b111".to_i # 0
y = "0111".to_i # 0
z = "0x111".to_i # 0
Однако у метода to_i есть необязательный второй параметр для указания основания. Обычно применяют только четыре основания: 2, 8, 10 (по умолчанию) и 16. Впрочем, префиксы не распознаются даже при определении основания.
x = "111".to_i(2) # 7
y = "111".to_i(8) # Восьмеричное - возвращает 73.
z = "111".to_i(16) # Шестнадцатеричное - возвращает 291.
x = "0b111".to_i # 0
y = "0111".to_i # 0
z = "0x111".to_i # 0
Из-за «стандартного» поведения этих методов цифры, недопустимые при данном основании, обрабатываются по-разному:
x = "12389".to_i(8) # 123 (8 игнорируется).
y = Integer("012389") # Ошибка (8 недопустима).
Хотя полезность этого и сомнительна, метод to_i понимает основания вплоть до 36, когда в представлении числа допустимы все буквы латинского алфавита. (Возможно, это напомнило вам о base64-кодировании; дополнительную информацию по этому поводу вы найдете в разделе 2.37.)
x = "123".to_i(5) # 66
y = "ruby".to_i (36) # 1299022
Для преобразования символьной строки в число можно также воспользоваться методом scanf из стандартной библиотеки, которая добавляет его в модуль Kernel, а также классы IO и String:
str = "234 234 234"
x, y, z = str.scanf("%d %o %x") # 234, 156, 564
Метод scanf реализует всю имеющую смысл функциональность стандартных функций scanf, sscanf и fscanf из библиотеки языка С. Но строки, представляющие двоичные числа, он не обрабатывает.
2.25. Кодирование и декодирование строк в кодировке rot13
Rot13 — наверное, самый слабый из известных человечеству шифров. Исторически он просто препятствовал «случайному» прочтению текста. Он часто встречается в конференциях Usenet; например, так можно закодировать потенциально обидную шутку или сценарий фильма «Звездные войны. Эпизод 13» накануне премьеры. Принцип кодирования состоит в смещении символов относительно начала алфавита (латинского) на 13: А превращается в N, В — в О и т.д. Строчные буквы смещаются на ту же величину; цифры, знаки препинания и прочие символы игнорируются. Поскольку 13 — это ровно половина от 26 (число букв в латинском алфавите), то функция является обратной самой себе, то есть ее повторное применение восстанавливает исходный текст.
Ниже приведена реализация этого метода, добавленного в класс String, никаких особых комментариев она не требует:
class String
def rot13
self.tr("A-Ma-mN-Zn-z","N-Zn-zA-Ma-m")
end
end
joke = "Y2K bug"
joke13 = joke.rot13 # "L2X oht"
episode2 = "Fcbvyre: Naanxva qbrfa'g trg xvyyrq."
puts episode2.rot13
2.26. Шифрование строк
Иногда нежелательно, чтобы строки можно было легко распознать. Например, пароли не следует хранить в открытом виде, какими бы ограничительными ни были права доступа к файлу.
В стандартном методе crypt применяется стандартная функция с тем же именем для шифрования строки по алгоритму DES. Она принимает в качестве параметра «затравку» (ее назначение то же, что у затравки генератора случайных чисел). На платформах, отличных от UNIX, параметр может быть иным.