HTML page

Глава 2 Числа

2.4. Преобразования между двоичной и десятичной системами счисления

Проблема

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

Решение

Чтобы преобразовать целое число Perl в строку, состоящую из единиц и нулей, сначала упакуйте его в сетевой формат "N" (с начальным старшим байтом), а затем снова распакуйте по одному биту (формат "В32").
sub dec2bin {
my $str = unpack("B32", pack("N", shift));
$str =~ s/"0+C7=\d)//; # В противном случае появятся начальные нули
return $str;
}

Чтобы преобразовать строку из единиц и нулей в целое число Perl, допилнип; ее необходимым количеством нулей, а затем выполните описанную выше процедуру в обратном порядке:
sub bin2dec {
return unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
}

Комментарий

Речь идет о преобразовании чисел между строками вида "00100011" и десятичной системой счисления (35). Строка содержит двоичное представление числа. На этот раз функция sprintfue поможет: в ней не предусмотрен формат для вывода чисел в двоичной системе счисления. Следовательно, нам придется прибегнуть к функциям Perl pack и unpack для непосредственных манипуляций со строковыми данными. Функции pack и unpack предназначены для работы со строками. Строки можно интерпретировать как последовательности битов, байты, целые, длинные целые, числа с плавающей запятой в представлении IEEE, контрольные суммы - не говоря уже о многом другом. Обе функции, pack и unpack, по аналогии со spnntf получают форматную строку, которая определяет выполняемые с аргументом операции. Мы используем pack и unpack для интерпретации строк как последовательностей битов и двоичного представления целого числа. Чтобы понять, каким образом строка интерпретируется как последовательность битов, необходимо хорошо разобраться в поведении функции pack. Строка интерпретируется как последо вательность байтов, состоящих из восьми бит. Байты всегда нумеруются слев:1 направо (первые восемь бит образуют первый байт, следующие восемь бит - второй и т. д.), однако внутри каждого байта биты могут нумероваться как слева направо, так и справа налево. Функция pack с шаблоном "В" работает с битами каждого байта, пронумеро ванными слева направо. Именно в этом порядке они должны находиться для при менения формата "N", которым мы воспользуемся для интерпретации последовательности битов как 32-разрядного целого.
$num = bin2dec('0110110') # $num = 54
$binstr = dec2bin(54); # $binstr = 110110

2.5. Действия с последовательностями целых чисел

Проблема

Требуется выполнить некоторую операцию со всеми целыми между Х и Y. Подобная задача возникает при работе с непрерывной частью массива или в любой ситуации, когда необходимо обработать все числа' из заданного интервала.

Решение

Воспользуйтесь циклом for или . . в сочетании с циклом to reach:
foreach ($X .. $Y) {
# $_ принимает все целые значения от Х до Y включительно
}
foreach $i ($X .. $Y) {
# $i принимает все целые значения от Х до Y включительно }
foreach ($i = $X: $i <= $y; $i++) {
# $i принимает все целые значения от X до Y включительно
}
foreach ($1 = $Х; $i <= $y; $i+=7) {

# $i принимает целые значения от Х до Y включительно с шагом 7 }

Комментарий

В первых двух методах используется конструкция $Х. . $Y, которая создает список всех целых чисел между $Х и $Y. Если $Х и $Y расположены далеко друг от друга, это приводит к большим расходам памяти (исправлено в версии 5.005). При организации перебора последовательных целых чисел цикл for из третьего способа расходует память более эффективно. В следующем фрагменте продемонстрированы все три способа. В данном случае мы ограничиваемся выводом сгенерированных чисел:
print "Infancy is:";
foreach (0 .. 2) { print "$_ ";
} print "\n";
print "Toddling is: ";
foreach $i (3 .. 4) {

Точнее, все целые числа. Найти все вещественные числа будет нелегко. Не верите - посмотрите у Кантора.

print "$i ";
} print "\n";
print "Childhood is: ":
for ($i = 5; $i <= 12; $i++) { print "$i ";
} print "\n";
Infancy is: 0 1 2
Toddling is: 3 4
Childhood is: 5 6 7 8 9 10 11 12


[> Смотри также -------------------------------
Описание операторов for и foreach в perlsyn(1).

2.6. Работа с числами в римской записи

Проблема

Требуется осуществить преобразование между обычными числами и числами в римской записи. Такая необходимость часто возникает при оформлении сносок и нумерации страниц в предисловиях.

Решение


Воспользуйтесь модулем Roman с CPAN:
use Roman;
$roman = roman($arabic); # Преобразование
# в римскую запись $arabic = arabic($roman) if isroman($roman); # Преобразование
# из римской записи

Комментарий

Для преобразования арабских ("обычных") чисел в римские эквиваленты в модуле Roman предусмотрены две функции, Roman и roman. Первая выводит символы в верхнем регистре, а вторая - в нижнем. Модуль работает только с римскими числами от 1 до 3999 включительно. В римской записи пет отрицательных чисел или нуля, а для числа 5000 (с помощью которого представляется 4000) используется символ, не входящий в кодировку ASCII.

use Roman;
$roman_fifteen = roman(15); # "xv" print "roman for fifteen is $roman_fifteen\n";
$arabic_fifteen = arabic($roman_fifteen);
print "Converted back, $roman_fifteen is $arabic_fifteen\n";
Roman for fifteen is xv Converted back, xv is 15


> Смотри также -------------------------------
Документация по модулю Roman; рецепт 6.23.

2.7. Генератор случайных чисел

Проблема

Требуется генерировать случайные числа в заданном интервале - например, чтобы выбрать произвольный элемент массива, имитировать бросок кубика в игре или сгенерировать случайный пароль.

Решение

Воспользуйтесь функцией Perl rand. $random = int( rand( $y-$x+1 ) ) + $x; o Комментарий Следующий фрагмент генерирует и выводит случайное число в интервале от 25 до 75 включительно:

$random = int( rand(51)) + 25;
print "$random\n";
Функция rand возвращает дробное число от 0 (включительно) до заданного аргумента (не включается). Мы вызываем ее с аргументом 51, чтобы случайное число было больше либо равно 0, но никогда не было бы равно 51 и выше. Затем от сгенерированного числа берется целая часть, что дает число от 0 до 50 включительно (функция int превращает 50,9999... в 50). К полученному числу прибавляется 25, что дает в результате число от 25 до 75 включительно. Одно из распространенных применений этой методики - выбор случайного элемента массива:

$elt = $array[ rand @array ];


Также она часто используется для генерации случайного пароля из заданной последовательности символов: @chars = ( "А" .. "z", "а" .. "z", 0 . . 9, qw(% ! 0 $%"&*)): $password = join("", @chars[ map { rand ochars } ( 1 .. 8 ) ]); Мы генерируем восемь случайных индексов @chars с помощью функции тар, извлекаем соответствующие символы в виде среза и объединяем их в случайный пароль. Впрочем, в действительности пароль получается не совсем случайным - безопасность вашей системы зависит от стартового значения (seed) генератора случайных чисел на момент запуска программы. В рецепте 2.8 показано, как "раскрутить" генератор случайных чисел и сделать генерируемые числа более случайными.

> Смотри также
Описание функций int, rand и join Bperlfunc(1). Случайные числа исследуются в рецептах 2.8-2.10, а используются - в рецепте 1.9.

2.8. Раскрутка генератора случайных чисел

Проблема

При каждом запуске программы вы получаете один и тот же набор "случайных" чисел. Требуется "раскрутить" генератор, чтобы Perl каждый раз генерировал разные числа. Это важно практически для любых применений случайных чисел, особенно для игр.

Решение


Воспользуйтесь функцией Perl srand:
srand EXPR;

Комментарий

Генерация случайных чисел - непростое дело. Лучшее, на что способен компьютер без специального оборудования, - генерация псевдослучайных чисел, равномерно распределенных в области своих значений. Псевдослучайные числа генерируются по математическим формулам, а это означает, что при одинаковом стартовом значении генератора две программы сгенерируют одни и те же псевдослучайные числа. Функция srand задает новое стартовое значение для генератора псевдослучайных чисел. Если она вызывается с аргументом, то указанное число будет использовано в качестве стартового. При отсутствии аргумента srand использует величину, значение которой трудно предсказать заранее (относится к Perl 5.004 и более поздним версиям; до этого использовалась функция time, значения которой совсем не были случайными). Не вызывайте srand в программе более одного раза. Если вы не вызвали srand сами, Perl версий 5.004 и выше вызывает srand с "хорошим" стартовым значением при первом запуске rand. Предыдущие версии этого не делали, поэтому программы всегда генерировали одну и ту же последовательность чисел. Если вы предпочитаете именно такое поведение, вызывайте srand с конкретным аргументом:
srand( );
То, что Perl старается выбрать хорошее стартовое значение, еще не гарантирует криптографической безопасности сгенерированных чисел от усердных попыток взлома. Информацию о построении надежных генераторов случайных чисел можно найти в учебниках по криптографии.

> Смотри также --------------------------------
Описание функции srand в perlfunc(1). Примеры ее применения приведены в рецептах 2.7 и 2.9.
copyright 2000 Soft group