Разница между "и" в Perl - PullRequest
4 голосов
/ 23 мая 2019

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

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

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

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

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

perl печатает разные значения?

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

Ответы [ 4 ]

6 голосов
/ 23 мая 2019

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

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

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

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

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

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

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

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

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

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

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

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

Итак, ради любви к Богу, не пишите такой код.

4 голосов
/ 23 мая 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 не был заменен фидом формы, как это обычно бывает в двойных кавычкахстроковые литералы.

2 голосов
/ 23 мая 2019

В обоих случаях $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'}" не было.

0 голосов
/ 23 мая 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
Кто-то может заметить, что этот ответ не отвечает на мой вопрос напрямую. Вы правы. Я публикую результаты теста двух разных методов доступа к значению переменной здесь, потому что считаю эти выводы важными.

...