I do not understand autoincrementing letters in Perl.
This example seems perfectly understandable:
$a = 'bz'; ++$a;
ca #output
b
gets incremented to c
. There is nothing left for z
to go to, so it goes back to a
(or at least this is how I see the process).
But then I come across statements like this:
$a = 'Zz'; ++$a;
AAa #output
and:
$a = '9z'; ++$a;
10 #output
Why doesn't incrementing Zz
return Aa
? And why doesn't incrementing 9z
return 0z
?
Thanks!
Процитируем perlop :
Если, однако, переменная была используется только в строковом контексте, поскольку он был установлен и имеет значение, отличное от пустая строка и соответствует шаблон
/ ^ [a-zA-Z] * [0-9] * \ z /
, приращение выполняется в виде строки, сохранение каждого персонажа в его диапазон, с переноской.
Диапазон значений: 0–9, A – Z и a – z. Когда нужен новый символ, он берется из диапазона первого символа. Каждый диапазон независим; символы никогда не покидают диапазон, в котором они начали.
9z
не соответствует шаблону, поэтому он получает числовое приращение. (Вероятно, он должен выдавать предупреждение «Аргумент не числовой», но этого не происходит в Perl 5.10.1.) Цифры разрешены только после всех букв (если есть), никогда перед ними .
Обратите внимание, что строка, состоящая из всех цифр , соответствует шаблону, а получает приращение строки (если оно никогда не использовалось в числовом контексте). Однако результат приращения такой строки идентичен числовому приращению, за исключением того, что он имеет бесконечную точность и ведущие нули (если есть) сохраняются. (Таким образом, вы можете увидеть разницу только тогда, когда количество цифр превышает то, что может хранить IV или NV, или если оно имеет начальные нули.)
Я не понимаю, почему вы думаете, что Zz
должно стать Aa
(если вы не думаете о модульной арифметике, но это не так). В результате этого процесса он становится AAa
:
z
оборачивается до a
. Увеличение предыдущего символа. Z
выполняется переход к A
. Предыдущего символа нет, поэтому добавьте первый из этого диапазона, то есть еще один A
. Оператор диапазона ( ..
), когда заданы две строки (и левая строка соответствует шаблону), использует приращение строки для создания списка (это объяснено в конце этого раздела). Список начинается с левого операнда, который затем увеличивается до тех пор, пока либо:
Возвращает список всех значений. (Если случай 2 завершает список, окончательное значение в него не включается.)
Потому что (игнорируя на данный момент регистр; регистр просто сохранен, с ним ничего интересного не происходит), «AA» является преемником «Z», так как же он может быть преемником для «ZZ»? Преемник ZZ - AAA.
Поскольку что касается ++
и всех других числовых операторов, «9z»
- это просто глупый способ написания 9, а преемником 9 является 10. специальное строковое поведение автоинкремента четко указано, что оно встречается только в строках букв или строках букв, за которыми следуют числа (и не смешиваются каким-либо другим образом).
Я не понимаю, почему увеличение Zz вернет Aa; как вы думаете, почему это должно быть? Увеличение 9z выглядит так, как будто Perl думает, что 9z - это число 9, а не какая-то странность с основанием 36.
Вы спрашиваете, почему инкремент не зацикливается.
Если бы это было так, это не было бы приращением. Увеличение означает, что у вас есть полностью упорядоченный набор и элемент в нем, и вы создаете следующий более высокий элемент, поэтому он никогда не сможет вернуть вас к более низкому элементу. В этом случае общий порядок - это стандартный алфавитный порядок строк (который определен только для английского алфавита), расширенный для работы с произвольными строками ASCII способом, который кажется естественным для некоторых распространенных типов строк идентификаторов.
Обертывание также лишило бы его цели: обычно вы хотите использовать его для генерации произвольного количества различных идентификаторов того или иного типа.
Я согласен с вердиктом Часа Оуэнса: применение этой операции к произвольным строкам - плохая идея, это не тот вид использования, для которого она была предназначена.
Я не согласен с его решением: просто выберите простое начальное значение, при котором инкремент будет вести себя нормально, и все будет в порядке.
Ответ - не делать этого. Автоматическое увеличение числа ++
не-числами полно неприятных ловушек. Подходит только для быстрых взломов.
Лучше написать собственный итератор для такого рода вещей:
#!/usr/bin/perl
use strict;
use warnings;
{ package StringIter;
sub new {
my $class = shift;
my %self = @_;
$self{set} = ["a" .. "z"] unless exists $self{set};
$self{value} = -1 unless exists $self{value};
$self{size} = @{$self{set}};
return bless \%self, $class;
}
sub increment {
my $self = shift;
$self->{value}++;
}
sub current {
my $self = shift;
my $n = $self->{value};
my $size = $self->{size};
my $s = "";
while ($n >= $size) {
my $offset = $n % $size;
$s = $self->{set}[$offset] . $s;
$n /= $size;
}
$s = $self->{set}[$n] . $s;
return $s;
}
sub next {
my $self = shift;
$self->increment;
return $self->current;
}
}
{
my $iter = StringIter->new;
for (1 .. 100) {
print $iter->next, "\n";
}
}
{
my $iter = StringIter->new(set => [0, 1]);
for (1 .. 7) {
print $iter->next, "\n";
}
}