выведите $ 1 = ~ s / - // в perl - PullRequest
0 голосов
/ 01 января 2012

Это приводит к ошибке «Попытка изменения значения только для чтения на ...»

$mfgs = "AUDBMWCH-FO-TOY";
while( $mfgs =~ /(.{3})/g ) {
    print $1 =~ s/-// . "\n";
}

Как мне это сделать без добавления дополнительных строк?Насколько я могу судить, Perl не имеет встроенной функции str_replace().Я мог бы просто написать один, но, как я уже сказал, я пытаюсь понять, как это сделать без дополнительных строк кода.

Это не используется в реальном проекте.Это используется только в учебных целях.



Ответы [ 6 ]

6 голосов
/ 01 января 2012

Давайте сделаем шаг назад

$mfgs = "AUDBMWCH-FO-TOY";
while( $mfgs =~ /(.{3})/g ) {
    print $1 =~ s/-// . "\n";
}

просто читает строку $mfgs по три символа за раз и удаляет дефисы.

Можно переписать это следующим образом:

say for map { s|-||r }  $mfgs =~ /.../g ; # Works in Perl 5.14+

use List::MoreUtils 'apply';
say for apply { s|-|| } $mfgs =~ /.../g ; # If that 'r' flag isn't there

или используйте оператор транслитерации (tr///), видя, что по сути ничего не происходит в регулярном выражении:

say for map tr!-!!dr , $mfgs =~ /.../g ;

Оба способа дали бы одинаковые результаты, если в блоке из трех символов имеется только один дефис. Это потому, что tr/-//dr удалит все дефисы, а s/-//r удалит только первое вхождение.


Это отвечает, как можно было сделать это иначе, поэтому давайте посмотрим, почему это не сработало раньше

Почему нельзя $1 изменить?

Согласно perldoc perlvar (выделение добавлено):

$<digits> ($1, $2, ...)

Содержит подшаблон из соответствующего набора захвата скобки из последнего успешного сопоставления с образцом, не считая шаблоны сопоставляются во вложенных блоках, которые уже были завершены.

Эти переменные только для чтения и динамически ограничены.

Другими словами, $1 нельзя изменить, это то, что s/// пытался сделать.

Однако, копия $1 может быть изменена, что в некоторой степени рассматривается в решении Джонатана Леффлера .

3 голосов
/ 01 января 2012

В Perl 5.14 есть новый модификатор /r, доступный для замен s/// (а также транслитераций tr/// / y///), который приводит к тому, что возвращаемый результат будет новой строкой, не изменяя исходную .

use 5.014;

$mfgs = "AUDBMWCH-FO-TOY";
while ($mfgs =~ /(.{3})/g) {
    say $1 =~ s/-//r;
}
3 голосов
/ 01 января 2012

Я не уверен, почему вы хотите это сделать - структура данных неоптимальна по сравнению с:

my @mfgs = ( "AUD", "BMW", "CH", "FO", "TOY" );

Однако это работает:

use strict;
use warnings;
my($mfgs, $x) = ("AUDBMWCH-FO-TOY");
while ($mfgs =~ /(.{3})/g)
{
    printf "%s\n", ($x = $1, $x =~ s/-//, $x);  
}

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

my $mfgs = "AUDBMWCH-FO-TOY";
while ($mfgs =~ /(.{3})/g)
{
    printf "%s\n", ($x = $1, $x =~ s/-//, $x);
}

Однако лучше даже не беспокоиться о том, как можно (ab) использовать Perl без use strict; и use warnings; (или use diagnostics;).

2 голосов
/ 01 января 2012

Как мне это сделать без добавления дополнительных строк? Насколько я могу сказать Perl не имеет встроенной функции str_replace (). Я мог бы просто написать один, но, как я уже сказал, я пытаюсь выяснить, как это сделать без дополнительных строк кода.

Вы читали perlfunc ? Кроме того, речь идет не о функциях, а о $1, являющейся переменной только для чтения. См. perldoc perlvar .

$<digits> ($1, $2, ...)

Содержит подшаблон из соответствующего набора захвата скобки из последнего успешного сопоставления с образцом, не считая шаблоны сопоставляются во вложенных блоках, которые уже были завершены.

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


Насколько я могу судить, это наиболее экономичный способ получить код, о котором идет речь:

tr/-//d, say for $mfgs =~ /.{3}/g;

Или длинная версия:

my $mfgs = "AUDBMWCH-FO-TOY";
for my $str ($mfgs =~ /.{3}/g) {
    $str =~ tr/-//d;
    say $str;
}

Обратите внимание, что в этом случае захват скобок не требуется. От perldoc perlop :

Модификатор / g определяет глобальное сопоставление с образцом, то есть сопоставление столько раз, сколько возможно внутри строки. Как это ведет себя, зависит от контекст. В контексте списка возвращает список подстрок сопоставляется с любыми захватывающими скобками в регулярном выражении. Если нет круглых скобок, он возвращает список всех совпавших строки, как если бы вокруг всего шаблона были круглые скобки.

Также обратите внимание на разницу между while и for (foreach). while будет перебирать совпадения по мере их появления (используя /g в скалярном контексте) с неявным использованием утверждения \G, но не будет сохранять совпадение в переменной (за исключением $1). for извлечет все совпадения одновременно (используя /g в контексте списка), и совпадение будет совмещено в переменной цикла ($_ в первом случае, my $str во втором).

В скалярном контексте каждое выполнение m // g находит следующее совпадение, возвращает значение true, если оно совпадает, и значение false, если дальнейшее совпадение отсутствует. Положение после последнего совпадения можно прочитать или установить с помощью pos () функция; см. поз. Неудачное совпадение обычно сбрасывает позицию поиска в начале строки, но вы можете избежать этого, добавив Модификатор / c (например, m // gc). Изменение целевой строки также сбрасывает позиция поиска.

2 голосов
/ 01 января 2012
print( do { ( my $x = $1 ) =~ s/-//; $x }, "\n" );
print( ( apply { s/-// } $1 ), "\n" );
say( do { ( my $x = $1 ) =~ s/-//; $x } );  # 5.10+
say( apply { s/-// } $1 );                  # 5.10+
say( $x =~ s/-//r );                        # 5.14+

apply взято из Список :: MoreUtils .

0 голосов
/ 01 января 2012

Вы всегда можете просто сделать это, чтобы получить массив элементов, разделенных по вашему разделителю.

@a = split("-", $mfgs);

Для str_replace вы используете

$var =~ s/search/replace/g;

Примерно так:

$mfgs =~ s/-/\n/g;
printf("%s\n", $mfgs);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...