Автовосстановление писем в Perl

Я не понимаю автоинкрементных букв в Perl.

Этот пример кажется вполне понятным:

$a = 'bz'; ++$a;
ca #output

b увеличивается до c. Для z ничего не осталось, поэтому он возвращается к a (или, по крайней мере, так я вижу процесс).

Но затем я нахожу такие утверждения, как это:

$a = 'Zz'; ++$a;
AAa #output

и

$a = '9z'; ++$a;
10 #output

Почему не увеличивается Zz return Aa? И почему не увеличивается 9z return 0z?

Спасибо!

Ответ 1

Чтобы процитировать 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.

Оператор диапазона (..), когда заданы две строки (и левая часть соответствует шаблону), использует строку increment для создания списка (это объясняется в конце этого раздела). Список начинается с левого операнда, который затем увеличивается до тех пор, пока:

  • Значение равно правому операнду или
  • Длина значения превышает длину правого операнда.

Он возвращает список всех значений. (Если случай 2 завершает список, окончательное значение не включается в него.)

Ответ 2

  • Потому что (игнорируя случай на данный момент, случай просто сохраняется, ничего интересного с ним не происходит), "AA" является преемником "Z", так как же он может быть преемником "ZZ"? Преемником "ZZ" является "AAA".

  • Так как для ++ и всех других числовых операторов, "9z" - это просто глупый способ записи 9, а преемником 9 является 10. Специальное строковое поведение автоматического приращения явно указано только для строк букв или строк букв, за которыми следуют номера (и не смешиваются каким-либо другим способом).

Ответ 3

Ответ: не делать этого. Автоматическое приращение ++ с не числами полна неприятных ловушек. Он подходит только для быстрого взлома.

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

#!/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";
    }
}

Ответ 4

Вы спрашиваете, почему приращение не обертывается.

Если бы это было так, это не было бы приращением. Для увеличения означает, что у вас есть полностью упорядоченный набор и элемент в нем и создайте следующий более высокий элемент, поэтому он никогда не сможет вернуть вас к нижнему элементу. В этом случае полное упорядочение представляет собой стандартное алфавитное упорядочение строк (которое определяется только на английском алфавите), расширенное, чтобы справиться с произвольными строками ASCII таким образом, который кажется естественным для некоторых общих типов строк идентификатора.

Обтекание также победит его цель: обычно вы хотите использовать его для генерации произвольно многих разных идентификаторов.

Я согласен с вердиктю Chas Owens: применение этой операции к произвольным строкам - плохая идея, что не тот вид использования, для которого она предназначалась.

Я не согласен с его лекарством: просто выберите простое начальное значение, по которому приращение ведет себя здорово, и все будет хорошо.

Ответ 5

Я не понимаю, почему приращение Zz вернет Aa; почему вы думаете, что нужно? Приращение 9z выглядит так, как будто Perl считает, что 9z - это число 9, а не какая-то странность в основе 36.