Разница между "и" в Perl

Согласно этому комментарию:

Вы также не должны использовать одинарные кавычки в print ">>${ '_<$filename' }<<\n". Вместо этого попробуйте: print ">>${ \"_<$filename\" }<<\n"

Я всегда думал, что различия между " и ' заключаются только в том, что строка интерпретируется или нет.

Я хочу спросить, почему в этом контексте:

print ">>${ \"_<$filename\" }<<\n"
print ">>${  '_<$filename'  }<<\n"

perl выводит разные значения?

Почему я должен использовать \" вместо ' здесь?


person Eugen Konkov    schedule 23.05.2019    source источник
comment
Я предполагаю, что это потому, что "${ \"_<$filename\" }" анализируется дважды. Если вы используете одинарные кавычки внутри, он не будет интерполировать $filename (поскольку одинарные кавычки не интерполируются)?   -  person Håkon Hægland    schedule 23.05.2019


Ответы (4)


Что происходит, так это то, что в обоих случаях $filename интерполируется не внешней строкой (">>...<<\n"), а скорее ${ ... }. (это просто вывод из поведения). Это означает, что в ">>${ '_<$filename' }<<\n" $filename вообще не интерполируется, а в ">>${ \"_<$filename\" }<<\n" есть.

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

$filename = "ab'cd";

Если бы интерполяция выполнялась внешней строкой, то "${'$filename'}" было бы эквивалентно "${'ab'cd'}", что является синтаксической ошибкой. Или, может быть, "${ab'cd}", что эквивалентно "${ab::cd}"; тоже не то, что ты хочешь.

Тогда как если интерполяция выполняется с помощью ${}, то в "${'$filename'}" ${...} на самом деле будет ${"$filename"} (без экранированных двойных кавычек), что интерполирует $filename, и вы получите что-то вроде ${"ab'cd"}, как вы и хотели.


Рассмотрим этот пример:

$abcd = "First var";
${'$v'} = "Second var";
$v = "abcd";

say "${\"$v\"}"; # prints "First var"
say "${'$v'}";   # prints "Second var"

Это показывает, что с "${\"$v\"}" было интерполировано $v, а с "${'$v'}" — нет.

person Dada    schedule 23.05.2019
comment
значение $filename равно Specio::Constraint::Simple->_optimized_constraint - person Eugen Konkov; 23.05.2019
comment
@EugenKonkov это не имеет значения: мой пример показывает, почему интерполяция должна выполняться по ${}, а не по "". Для Perl было бы нелепо смотреть на значение $filename и на его основе решать, где нужно выполнить интерполяцию. - person Dada; 23.05.2019
comment
теперь пример отвечает на вопрос. Задокументировано ли, что одинарные кавычки внутри строки с двойными кавычками переключают интерполяцию? - person Eugen Konkov; 23.05.2019
comment
@EugenKonkov Я не нашел никакой документации по этому поводу ни в perldata, ни в perlop. Я не знаю, задокументировано ли это где-нибудь. - person Dada; 23.05.2019

Краткий ответ на ваш вопрос заключается в том, что $filename интерполируется в вашем первом примере, а во втором - нет. Почему ты спрашиваешь? О, парень. Пристегнитесь для ухабистой езды.

Во-первых, поймите, для чего предназначена нотация ${...}. Есть две причины для его использования: первая — просто отличить имя переменной от окружающего текста; другой - разыменовать значение, возвращаемое БЛОКОМ (кода).

Если содержимое скобок — это просто пустое слово (с возможными пробелами вокруг него), то это просто имя переменной. Таким образом, строка "$foo", строка "${foo}" и строка "${ foo }" дают один и тот же результат. Это удобно, если вам нужно добавить текст непосредственно после переменной в строке: вместо $foo.'bar' вы можете сказать "${foo}bar". Обозначения работают и вне интерполирующих строк: вы можете сказать ${foo} = 'bar', если хотите.

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

Теперь поймите, что когда Perl достигает нотации "${" внутри вашей интерполируемой строки, он должен принять решение о том, что он здесь интерполирует: обычное имя переменной или код. Без интерполяции чего-либо еще выполняется поиск соответствующей закрывающей скобки. Если подстрока, заключенная в эти скобки, не выглядит голым словом, то она будет оценена. Вам по-прежнему нужно избегать двойных кавычек в этой области, потому что любая неэкранированная двойная кавычка будет считаться последним разделителем для внешней строки, подмножеством которой является это, а оставшаяся часть вашего «блока» будет вне строки.

Имея это в виду, мы видим, что ваш первый пример оценивается как "_<$filename", тогда как ваш второй пример оценивается как '_<$filename'. В этом разница между интерполирующей строкой и нет: в первой $filename заменено на что-то, а во второй строго буквальная.

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

Если вы думаете, что я шучу о том, что содержимое фигурных скобок эффективно оценивается, попробуйте этот код.

print "This, ${die qq(oops!\n)}, and this.\n";

Конечно, ключевое различие между подобным кодом в строке и собственно eval() заключается в том, что eval() перехватит исключение и установит $@. Это не так: он просто умирает, как в обычном блоке кода.

Это порождает один из скрытых приемов Perl. Вы не можете интерполировать вызов метода, например $foo->bar, но вы можете разыменовать ссылку на содержащий его массив, например.

print "@{[$foo->bar]}\n";

Подводя итог, ради Бога, пожалуйста, не пишите такой код.

person TFBW    schedule 23.05.2019

Ответ довольно прост: символы $ и @ не имеют особого значения, когда встречаются в коде в строковом литерале.

">>${ \"_<$filename\" }<<\n" эквивалентно ">>" . ${ "_<$filename" } . "<<\n".

">>${ '_<$filename'" }<<\n" эквивалентно ">>" . ${ '_<$filename' } . "<<\n".

Как видите, $filename не было заменено значением $filename, как это обычно бывает в строках с двойными кавычками.

Если это не ясно, давайте рассмотрим гораздо более распространенный пример.

">>$h{$key}<<" эквивалентно ">>" . $h{$key} . "<<".

">>$h{\"$key\"}<<" эквивалентно ">>" . $h{"$key"} . "<<".

">>$h{'$key'}<<" эквивалентно ">>" . $h{'$key'} . "<<".

Теперь представьте, что произойдет, если вместо этого будет интерполировано $key. (Например, используйте $key = "2+3";.) Поведение будет гораздо более неожиданным и гораздо более опасным.


Точно так же символ \ не имеет особого значения, когда встречается в коде в строковых литералах, если только он не экранирован разделителем.

"foo ${\f()} bar" эквивалентно "foo " . ${\f()} . " bar"

Как видите, \f не было заменено переводом формы, как это обычно происходит в строковых литералах с двойными кавычками.

person ikegami    schedule 23.05.2019

Это только мое предположение, и я не могу найти никакой официальной документации по этому поводу, но, похоже, это работает.

Также ilmari говорит, что я должен go via the symbol table:

${$main::{'_<foo::bar::baz'}}

Таким образом мой код разбивается на два случая:.

СЛУЧАЙ 1:

Devel::Peek::Dump( ${       "::_<$filename"          } );
Devel::Peek::Dump( ${       '::_<' ."$filename"      } );
Devel::Peek::Dump( ${       "::_<Specio::Constraint::Simple->_optimized_constraint"  } );
Devel::Peek::Dump( ${       '::_<Specio::Constraint::Simple->_optimized_constraint'  } );

Все это одно и то же. Двойные кавычки или одинарные кавычки: это не имеет значения.

SV = PV(0xe64ab00) at 0xe300bc8
  REFCNT = 1
  FLAGS = ()
  PV = 0

СЛУЧАЙ 2:

Devel::Peek::Dump( ${ ${ 'main::' }{ "_<$filename" } } );
Devel::Peek::Dump( ${       ${'::'}{ "_<$filename" } } );
Devel::Peek::Dump( ${           $::{ "_<$filename" } } );
Devel::Peek::Dump( ${     $::{"_<Specio::Constraint::Simple->_optimized_constraint"} } );
Devel::Peek::Dump( ${     $::{'_<Specio::Constraint::Simple->_optimized_constraint'} } );

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

SV = PV(0x15e0a40) at 0x186cd10
  REFCNT = 1
  FLAGS = (POK,pPOK)
  PV = 0x1934880 "Specio::Constraint::Simple->_optimized_constraint"\0
  CUR = 49
  LEN = 51

Выводы
Не имеет значения, как заключать в кавычки имя переменной. Разница только в том, как мы идем:

  1. непосредственный
  2. через таблицу символов

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

    Devel::Peek::Dump( ${ $::{ "$name" } } );
    Devel::Peek::Dump( ${    "::$name"   } );

оба будут ссылаться на одно и то же.

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

UPD
Кто-то может заметить, что этот ответ не отвечает на мой вопрос напрямую. Ты прав. Я публикую результаты тестирования двух разных методов доступа к значению переменной здесь, потому что считаю эти выводы важными.

person Eugen Konkov    schedule 23.05.2019
comment
Хотя это интересно, я не понимаю, как это отвечает на ваш вопрос, который сводился к разнице между \"_<$filename\" и '_<$filename' внутри ${} внутри строки. Во всех примерах вашего ответа вы используете двойные кавычки вокруг $filename. - person Dada; 23.05.2019
comment
Может быть, это должен был быть ответ на ваш предыдущий вопрос? - person Dada; 23.05.2019
comment
@Dada: не во всех примерах есть ' вокруг имени файла. Уведомление жестко закодировано: Devel::Peek::Dump( ${ '::_<Specio::Constraint::Simple->_optimized_constraint' } ); - person Eugen Konkov; 24.05.2019
comment
@Dada: я согласен, что это не отвечает на мой вопрос напрямую. Здесь я показываю результаты тестирования некоторых аспектов ${ }. Я не собираюсь принимать этот ответ или около того. Не волнуйтесь. - person Eugen Konkov; 24.05.2019