Выбрать главу

n=ARGV[0].to_i

s = 0

while n > 0

     s += n%10

     n /= 10

end

puts s

Для опытного человека написание подобной программы не является проблемой, но новичок может сделать в ней несколько самых разных ошибок, поэтому будущих программистов знакомят с математическими методами проектирования циклов. Подобные знания зачастую абсолютно необходимы, но рассматриваемую задачу даже новичок сможет решить безошибочно, если воспользуется так называемым подходом Ruby (Ruby way). Он предполагает активное использование стандартных библиотек, что позволяет создавать короткие и ясные программы, не содержащие циклов, при проектировании которых так легко совершить ошибку.

Новая версия программы содержит последовательный вызов всего трёх методов и состоит из одной строки.

Метод scan[3] с параметром /./ преобразует строковое представление числа АRGV[0]в массив цифр, который обрабатывается методом inject – одним из так называемых итераторов, последовательно выполняющим действия, указанные в блоке (код в фигурных скобках), над каждым из элементов массива. При этом переменная s сначала инициализируется нулём, а на каждой итерации увеличивается на величину x.to_i, равную числовому значению очередной цифры х. Полученный результат затем печатается методом puts.

Отметим, что inject является методом модуля Enumerable, который включается (mix in) в различные классы, расширяя их возможности. Кроме массивов таким классом являются диапазоны (Ranges), чаще всего используемые в качестве последовательностей элементов: натуральные числа от 3 до 9 (3...9 или 3…10), латинские буквы от D до H (’D’..’H’) и т.п. При решении следующей задачи использование итератора each для диапазона чисел является вполне естественной идей.

Пример 4. Является ли простым число 15485863?

Напомним, что натуральное число является простым, если оно имеет ровно два различных делителя. Интерес к простым числам связан прежде всего с их использованием в криптографии. Подробнее об этом можно узнать на сайте http://en.wikipedia.org/wiki/Prime_numbers.

n=15_485_863

 (2...n).each do|i|

    if n % i == 0

      puts false; exit

    end

end

puts true

Наивный алгоритм проверки простоты числа n очевиден: будем последовательно находить остатки от деления n на числа 2, 3, 4 и так далее, вплоть до n-1. Если хотя бы один из остатков окажется нулевым, то число простым не является. Итератор each для каждого элемента диапазона выполняет блок (начинающийся do и заканчивающийся end[4]), проверяющий это условие и прекращающий выполнение программы (метод exit) в случае его выполнения. В Ruby операторы if, while и некоторые другие являются просто модификаторами выражений, что не так во многих других языках (обязательно ознакомьтесь с примерами на стр. 49).

Программа станет намного изящнее и понятнее, если воспользоваться методом any? (в Ruby имя метода может оканчиваться вопросительным знаком [5]) модуля Enumerable. Этот метод проверяет для элементов i диапазона (2...n) заданное в блоке условие n%i==0 и возвращает «истину» (true), как только встретит элемент, ему удовлетворяющий. Если же таких элементов не найдётся, то метод возвратит «ложь» (false). Отрицание (!) полученного результата является ответом на вопрос о простоте числа n.

n=15_485_863;put s !(2...n).any?{|i|n%i==0}

Аналогичный методу any? метод all? модуля Enumerable позволяет проверить истинность заданного в блоке условия для всех элементов коллекции. Этот модуль определяет ещё целый ряд методов, полный перечень можно получить с помощью команды ri Enumerable.

Пример 5. Найдите минимальное четырёхзначное число, уменьшающееся на 27 после перестановки его последней цифры на первую позицию.

puts(1000...10000).find{|x|x/10+1000*(x%10) == x-27}

Последняя цифра числа x – это x%10, а число, получающееся после её перестановки на первую позицию, равно значению выражения x/10+1000*(x%10). Таким образом, нам нужно найти (find) первое число из диапазона 1000…10000, для которого выполняется условие x/10+1000*(x%10) == x-27. Это делает метод find (или detect).

Заметим, что методы find_all и select, будучи применёнными к коллекции, возвращают массив всех тех её элементов, которые удовлетворяют заданному в блоке условию, а метод reject возвращает элементы, для которых условие оказывается ложным.

Пример 6. Напечатайте квадраты всех натуральных чисел, не больших тысячи, десятичная запись которых заканчивается на 396.

p(1..1000).map{|t|t*t}.select{|x|x%1000==396}

вернуться

[3]

Список всех методов класса String приведён в разделе Library Reference книги [3]. Команда ri String печатает его целиком, а команда ri String#scan – описание метода scan.

вернуться

[4]

Блок принято ограничивать do-end вместо {}, если его тело занимает несколько строк.

вернуться

[5]

Подробнее о вызовах методов рассказано на стр. 45.