Урок Ruby №9: «Если бы я был мультимиллиардером…»


Не знаю почему, но многие мои коллеги-программисты весьма скептически относятся к Ruby в плане работы с числами. И напрасно.

Мы с вами разобрались с тем, что в Ruby есть целые и вещественные числа. Однако часто в скриптах приходится высчитывать просто колоссальные величины. Если бы вы программировали на других языках, то наверняка бы ломали голову над типом, в котором нужно хранить такие данные. К счастью, в Ruby все значительно проще.

Помните, я в уроке 7 говорил о том, что в Ruby есть класс BigNum?Именно он и предназначен для хранения подобных величин. Как вы уже привыкли, интерпретатор автоматически распознает, какой тип данных использовать для вашей переменной и автоматически приводит FixNum к Bignum. Ради интереса попробуйте выполнить такой код:


type1=1000000

type2=type1**3

puts type1.class #выведется на экран Fixnum

puts type2.class #выведется на экран Bignum

Кстати, описанный прием годится для определения типа данных. Это еще нам пригодится в программировании в RoR. Как видите, для этого нужно всего лишь вызвать метод class у объекта. Как я и говорил, привыкайте мыслить категориями объектно-ориентированного программирования.

Тем не менее, если с целыми числами все достаточно просто, то вот с дробями уже нет. Вспомните любую периодическую дробь – количество знаков после запятой там просто поражает воображение. Конечно, можно округлить дробь, но как быть, если мы хотим использовать все это безумное количество цифр?

Использовать класс Float нерационально, так как он уже внесет погрешность. К счастью, в Ruby есть для этих целей классы BigDecimal и Rational. Сейчас мы рассмотрим их применение.

BigDecimal позволяет нам высчитывать большие дроби с адекватной скоростью. Этот класс не предназначен для операций с периодической дробью, однако во всех остальных случаях он актуален.

Интересно, что этот объект инициализируется строкой, хотя на деле является массивом цифр. Благодаря этому достигается произвольная точность. К счастью для программиста, можно не вникать в эти детали. Тем не менее, для эффективного использования языка важно понимать, что у него под капотом.

К сожалению, в Ruby при сравнении вещественных чисел возникает казус, что при сравнении двух вроде бы одинаковых с виду дробей, одну из которых мы получили при расчете, язык посчитает, что они совершенно разные. Подробнее об этом вы можете прочитать в уроке 9. Это чревато логическими ошибками. Чтобы этого избежать, как раз и используют класс BigDecimal.

Для использования класса BigDecimal нужно подключить библиотеку bigdecimal. В Ruby библиотеки подключаются ключевым словом require. Посмотрите на такой код:


require ‘bigdecimal’ #подключаем библиотеку bigdecimal

a=BigDecimal (“3.2”)

b= BigDecimal (“2.0”)

c= BigDecimal (“1.2”)

if (a-b)==c then

puts “Все верно”

else

puts “Числа не равны”

end

Запустите скрипт и посмотрите на результат. На экран выведется «Все верно». Ради интереса, напишите такой же скрипт, только без BigDecimal. Результат вас несколько удивит.

 

Как я уже и говорил, библиотеку BigDecimal нужно подключить, что я и сделал в первой строчке. Без нее код бы не работал. В операторах присваивания я явным образом привел нашу дробь к типу BigDecimal. Если бы я этого не сделал, вы бы получили результатом скрипта фразу «Числа не равны».

Это очень простой пример пользы от BigDecimal, однако часто возникают ошибки как раз из-за этой особенности Ruby. Ну и напоследок хотелось бы подчеркнуть, что все три переменные из этого скрипта теперь стали объектами класса BigDecimal, а это означает, что они могут использовать его много численные методы методы.

Более подробно об этом вы можете почитать во встроенной документации по Ruby. В конце-концов, мы не будем с вами программировать сложные программы для расчетов. Единственное, что хотелось бы добавить – библиотека bigdecimal имеет ряд подбиблиотек. Это и подбиблиотека bigdecimal /math, bigdecimal /jacobian и т.д. Эти подбиблиотеки позволяют делать включают удобные модули с различными методами, которые сильно упрощают жизнь разработчику.

BigDecimal все же не удобен для работы с бесконечными дробями в силу своей погрешности при округлении. Поэтому в Ruby имеется класс Rational. В справочной документации об этом классе очень хорошо написано, поэтому я не буду дублировать ее. Тем не менее, вам важно знать, как пользоваться этим классом.

Учтите, что экземплярами этого класса должны быть только рациональные числа (частное от деления двух целых чисел). С иррациональными числами его использовать нельзя.

Для создания объекта Rational используется одноименный метод, в который мы передаем в качестве параметром делитель и делимое через запятую:


r=Rational (1,2) # r равно 0.5

r1=Rational (1,3) #0.333333333333…

r2=Rational (4,2) #2.0, тоже рациональное число

Не вздумайте передавать вторым параметром 0! Это ошибка. Важно понять, что метод Rational возвращает объекту частное от деления и поэтому для него характерно то же самое, что и для арифметических правил.

А теперь давайте применим наши знания на практике. Вспомните пример из предыдущего урока:


a=1000001.0 /0.003

b=0.003*a

if b==10000001.0 then

puts “совпадение”

else

puts “несовпадение”

end

Ruby выдавал нам «несовпадение». А вот теперь смотрите как можно сделать «совпадение»


a=Rational (1000001,1) / Rational (3,1000)

b=Rational (3,1000) *x

if b==10000001.0 then

puts “совпадение”

else

puts “несовпадение”

end

Запустите скрипт на исполнение и посмотрите на результат. Теперь все работает как нужно.

<<Предыдущий урок                                                                        Следующий урок>>

Яндекс.Метрика