Приглашаем посетить
Ходасевич (hodasevich.lit-info.ru)

1.2. Выбор значения по умолчанию

Назад
Глава 1 Строки
Вперед

1.2. Выбор значения по умолчанию

Проблема

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

Решение

Воспользуйтесь оператором || или ||=, работающим как со строками, так и с числами:

# Использовать $b, если. Значение $b истинно, и $с в противном случае
$а = $b || $с;

# Присвоить $х значение $у. носишь в том случае,
# если $х не является истинной
$х ||= $у;

Если ваша переменная может принимать значения Одни "0", воспользуйтесь функцией defined

#использоватъ $b если значение $b определено , и $с в противном случае
$а =defined($b) ? $b : $c

Комментарий

Главное отличие между этими двумя приемами (defined и ||) состоит, прежде всего, в том, что именно проверяется - определенность или истинность, В мире Perl три определенных значения являются ложными: 0, "0" и "". Если ваша переменная содержит одну из этих величин, но вы не хотите изменять ее, 11 не подойдет - приходится выполнять неуклюжие проверки с defined. Часто бывает удобно организовать программу так, чтобы принималась в расч т истинность или ложность переменных, а не их определенность.

В отличие от других языков, где возвращаемые значения ограничиваются 0 и 1, оператор || Perl обладает более интересным свойством: он возвращает первый (левый) операнд, если тот имеет истинное значение; в противном случае возвращается второй операнд. Оператор && ведет себя аналогично (для второго выражения), но этот факт используется реже. Для операторов несущественно, что представляют собой их операнды - строки, числа или ссылки; подойдет любое скалярное значение. Они просто возвращают первый операнд, из-за которого все выражение становится истинным или ложным. Возможно, это расходится с возвращаемым значением в смысле булевой алгебры, но такими операторами удобнее пользоваться.

Это позволяет установить значение по умолчанию для переменной, функции или более длинного выражения в том случае, если первый операнд не подходит. Ниже приведен пример использования ||, в котором $foo присваивается либо $bar, либо, если значение $bаr ложно, - строка "DEFAULT VALUE":

$foo = $bar || "DEFAULT VALUE";

В другом примере переменной $dir присваивается либо первый аргумент командной строки программы, либо "/tmp", если аргумент не указан:

$dir = shift(@ARGV) || "/tmp";

То же самое можно делать и без изменения @ARGV:

$dir = $ARGV[0] || "/tmp";

Если 0 является допустимым значением $ARGV[0], использовать || нельзя, потому что вполне нормальное значение будет интерпретировано как ложное. Приходится обращаться к тернарному оператору выбора:

$dir = defined($ARGV[0]) ? shift(@"ARGV) : "/tmp";

To же можно записать и иначе, со слегка измененной семантикой:

$dir = @ARGV ? $ARGV[0] : "/tmp";

Мы проверяем количество элементов в @ARGV. В условии оператора выбора (?:) @ARGV интерпретируется в скалярном контексте. Значение будет ложным лишь при нулевом количестве элементов, в этом случае будет использоваться "/tmp". Во всех остальных ситуациях переменной (когда пользователь вводит аргумент) будет присвоен первый аргумент командной строки.

Следующая строка увеличивает значение %count, при этом в качестве ключа используется значение $shell, а если оно ложно - строка "/bin/sh".

$count{ $shell || "/bin/sh" }++;

В одном условии можно объединить несколько альтернативных вариантов, как показывает следующий пример. Результат совпадает с первым операндом, имеющим истинное значение.

# Определить имя пользователи  всистеме UNIX 
$user = $ENV{USER} 
	|| $ENV{LOGNAME}
	|| getlogin()
	|| (getwuid($<))[0]
	|| "Unknown uid number $<";

Оператор && работает аналогично; он возвращает первый операнд, если этот операнд ложен. В противном случае возвращается второй операнд. Поскольку ложные значения представляют интерес существенно реже, чем истинные, это свойство используется не так часто. Одно из возможных применений продемонстрировано в рецепте 8.13.

Оператор присваивания || = выглядит странно, но работает точно так же, как и остальные операторы присваивания. Практически для всех бинарных операторов perl $var ОР= value означает $var = $var op value; например, $а += $Ь-тоже, что и $а = $а + $Ь. Следовательно, оператор 11 = может использоваться для присваивания альтернативного значения переменной. Поскольку 11 выполняет простую логическую проверку (истина или ложь), у него не бывает проблем с неопределенными значениями, даже при использовании ключа -w.

В следующем примере ||= присваивает переменной $starting_point значение "greenwich", если оно не было задано ранее. Предполагается, что $starting_point не принимает значений 0 или "0", а если принимает - то такие значения должны быть заменены:

$starting_point ||= "greenwich";

В операторах присваивания || нельзя заменять оператором оr, поскольку or имеет слишком низкий приоритет. Выражение $а = $b or $с эквивалентно ($а = $b) or $c. В этом случае переменной $Ь всегда присваивается $а, а это совсем не то, чего вы добивались.

Не пытайтесь распространить это любопытное применение || и ||= со скалярных величин на массивы и хэши. У вас ничего не выйдет, потому что левый операнд интерпретируется в скалярном контексте. Приходится делать что-нибудь подобное:

@а = @b unless @a;      # Копировать, если массив пуст
@а = @b ? @b : @с;     # Присвоить @b, если он не пуст, иначе @с

Смотри также

Описание оператора || в perlop(1); описание функций defined и exists в perlfunc(1).

1.3. Перестановка значений без использования временных переменных

Проблема

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

Решение

Воспользуйтесь присваиванием по списку:

($VAR1, $VAR2) = ($VAR2, $VAR1);

Комментарий

В большинстве языков программирования перестановка значений двух переменных требует промежуточного присваивания:

$temp = $a;
$а    = $Ь;
$b    = $temp;

В Perl дело обстоит иначе. Язык следит за обеими сторонами присваивания и за тем, чтобы ни одно значение не было случайно стерто. Это позволяет избавиться от временных переменных:

$а="alpha";
$b="omega";
($а, $b) = ($Ь, $а);  # Первый становится последним - и наоборот

Подобным способом можно поменять местами сразу несколько переменных:

($alpha, $beta, $production) = qw(January March August);

# beta перемещается в alpha,
# production - в beta,
# alpha - в production
($alpha, $beta, $production) = ($beta, $production, $alpha);

После завершения этого фрагмента значения переменных $alpha, $beta и $production будут равны соответственно "March", "August" и "January".

Смотри также

Раздел "List value constructors" perlop(1).

1.4 Преобразование между символами и ASCII-кодами

Проблема

Требуется вывести код, соответствующий некоторому символу в кодировке ASCII, или наоборот - символ по ASCII-коду.

Решение

Воспользуйтесь функцией ord для преобразования символа в число или функцией сhr - для преобразования числа в символ:

$num = ord($char);
$char = chr($num);

Формат %с в функциях printf и sprintf также преобразует число в символ:

$char = sprintf ("%с", $num);         # Медленнее, чем chr($num) 
printf("number %d is character %c\n", $num, $num);
number 101 is character e

Шаблон С*, используемый в функциях pack и unpack, позволяет быстро преобразовать несколько символов:

@АSCII= unpack("c*". $string);
@STRING = pack("С*", $ascii);

Комментарий


В отличие от низкоуровневых, нетипизованных языков вроде ассемблера, Perl не считает эквивалентными символы и числа; эквивалентными считаются строки и числа. Это означает, что вы не можете произвольно присвоить вместо символа его числовое представление, или наоборот. Для преобразования между символами и их числовыми значениями в Perl существуют функции chr и ord, взятые из Pascal:

$ascii_value = ord("e");   # Теперь 101
$character   = chr(101);   # Теперь "e"

Символ в действительности представляется строкой единичной длины, поэтому его можно просто вывести функцией print или с помощью формата %s функций printf и sprintf. Формат %с заставляет printf или sprintf преобразовать число в символ, однако он не позволяет вывести символ, который уже хранится в символьном формате (то есть в виде строки).

printf("Number %d is character %c\n",101,101);

Функции pack" unpack, chr и ord работают быстрее, чем sprintf. Приведем, пример практического применения pack x unpack:

@ascii_character_numbers = unpack("C*", "sample"); 
print "@ascii_character_ numbers \n";
115  97 109 112  108 101

$word=pack("c*",@ascii_character_numbers);
$word = pack("c*",115,  97, 109, 112,  108, 101);  # То же самое
print "$word\n",
sample

А вот как превратить HAL в IBM:

$hal = "hal";
@ascii = unpack("c*", $hal); 
foreach $val (@ascii) {
	$val++;             # Увеличивает каждый  ASCII - код на 1
};
$ibm = pack("c*"), @ascii); 
print "$ibm\n";          # Выводит "IBM"

Функция ord возвращает числа oт 0 до 255. Этот диапазон соответствует типу данных unsigned char языка С.

Смотри также

Описание функций chr, ord, printf, sprintf, pack и unpack в perlfunc(1).


Назад
Вперед