Сохранение таблицы транслитерации в perl

Я хочу транслитерировать цифры от 1 до 8 с 0, но не знаю числа во время компиляции. Поскольку транслитерация не интерполирует переменные, я делаю это:

@trs = (sub{die},sub{${$_[0]} =~ tr/[0,1]/[1,0]/},sub{${$_[0]} =~ tr/[0,2]/[2,0]/},sub{${$_[0]} =~ tr/[0,3]/[3,0]/},sub{${$_[0]} =~ tr/[0,4]/[4,0]/},sub{${$_[0]} =~ tr/[0,5]/[5,0]/},sub{${$_[0]} =~ tr/[0,6]/[6,0]/},sub{${$_[0]} =~ tr/[0,7]/[7,0]/},sub{${$_[0]} =~ tr/[0,8]/[8,0]/});

а затем индексировать его как:

$trs[$character_to_transliterate](\$var_to_change);

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


person user246100    schedule 10.03.2011    source источник
comment
tr/[0,1]/[1,0]/ можно (должно) записать как tr/01/10/   -  person mob    schedule 10.03.2011
comment
Символы [,] ничего не добавляют к выражению (и могут сделать его менее эффективным). Это эквивалентно, скажем, tr/],[01/],[10/. Они могут ввести в заблуждение начинающего Perl-программиста, столкнувшегося с вашим кодом, что они как-то связаны с синтаксисом tr.   -  person mob    schedule 10.03.2011
comment
Оператор tr/// не понимает шаблоны регулярных выражений или классы символов ([a-z], [a-z0-9] и т. д.), он использует простые списки и диапазоны символов. Я рекомендую просмотреть perldoc perlop в командной строке или посетить perldoc.perl .org/ для освежения знаний.   -  person converter42    schedule 10.03.2011
comment
[0,1] работает правильно, он не менее эффективен и выглядит лучше, поэтому я буду придерживаться его.   -  person user246100    schedule 10.03.2011
comment
@user: [0,1] это может работать правильно, но это вводит в заблуждение. Прочтите второй комментарий моба и конвертера.   -  person Cascabel    schedule 10.03.2011
comment
@user246100 user246100, но он работает только случайно, и вы понятия не имеете, почему он работает, поэтому, когда он сломается, вы не поймете, почему он сломался.   -  person hobbs    schedule 10.03.2011
comment
когда он сломается, я пришлю тебе письмо с извинениями за то, что не послушал тебя   -  person user246100    schedule 10.03.2011
comment
@user246100: Вау, я не могу себе представить, какое удовольствие вы, должно быть, получаете, отслеживая segfaults, потому что вы совершенно случайно обрабатывали свою память так, чтобы она выглядела лучше и работала правильно... но тогда, возможно, вы не используете никаких языков где это может произойти.   -  person Cascabel    schedule 10.03.2011


Ответы (3)


Каждый раз, когда вы повторяетесь, вы должны посмотреть, можно ли сделать то, что вы делаете, в цикле. Поскольку tr создает свои таблицы во время компиляции, вы можете использовать eval для доступа к компилятору во время выполнения:

my @trs = (sub {die}, map {eval "sub {\$_[0] =~ tr/${_}0/0$_/}"} 1 .. 8);

my $x = 123;

$trs[2]($x);

print "$x\n"; # 103

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

Если вы не хотите использовать оценку строки, вам нужно использовать конструкцию, которая поддерживает модификацию во время выполнения. Для этого вы можете использовать оператор s///:

sub subst {$_[0] =~ s/($_[1]|0)/$1 ? 0 : $_[1]/ge}

my $z = 1230;

subst $z => 2;

print "$z\n"; # 1032

Конструкция tr/// работает быстрее, чем s///, так как последняя поддерживает регулярные выражения.

person Eric Strom    schedule 10.03.2011
comment
:D Мне нравится ваш код, но в итоге он такой же, как мой, но сжатый. Я больше искал другой способ сделать это. В любом случае, я буду очень признателен. - person user246100; 10.03.2011
comment
Какого типа another way of doing it вы ожидали? Это можно сделать с помощью s///, это можно сделать с помощью ленивой конструкции tr в подпрограмме вместо создания всех элементов массива, это можно сохранить в хеше для работы не только с числами... - person Eric Strom; 10.03.2011
comment
Способ без кода внутри строк, без повторения кода, без eval и с небольшим количеством кода. - person user246100; 10.03.2011

Я бы предложил просто отказаться от tr в пользу чего-то, что действительно позволяет немного метапрограммирования, например s///. Например:

# Replace $to_swap with 0 and 0 with $to_swap, and leave
# everything else alone.
sub swap_with_0 {
    my ($digit, $to_swap) = @_;
    if ($digit == $to_swap) {
        return 0;
    } elsif ($digit == 0) {
        return $to_swap;
    } else {
        return $digit;
    }
}

# Swap 0 and $to_swap throughout $string
sub swap_digits {
    my ($string, $to_swap) = @_;
    $string =~ s/([0$to_swap])/swap_with_0($1, $to_swap)/eg;
    return $string;
}

что удивительно просто. :)

person hobbs    schedule 10.03.2011
comment
Я думаю, вы пропустили скобки и модификатор g в замене - person Eugene Yarmash; 10.03.2011

Вот короткая подпрограмма, использующая замену вместо транслитерации:

sub swap_digits {
    my ($str, $digit) = @_;
    $str =~ s{ (0) | $digit }{ defined $1 ? $digit : 0 }gex;
    return $str;
}
person Sean    schedule 10.03.2011
comment
Это злой трюк, использующий захват или нет, чтобы отличить цифру от 0, и не очень подходит для новичка;) - person hobbs; 10.03.2011