Является ли соглашением избегать использования $ _ при использовании Perl API других людей? - PullRequest
9 голосов
/ 29 октября 2010

Меня только что поймали при использовании чужого API в сочетании с переменной по умолчанию $ _

foreach (@rps_server_details) {
    @server_data = ();
    @server_data = split(/,/);
    @$esp_hosts = ();
    $filters{server_name} = $server_data[0];
    print "--->$_<--\n";
    $esp_hosts = $esp->get_hosts(fields => $fields, %filters) || die "$@";
    print "--->$_<--\n";

Вывод для этого:

--->igrid8873.someone.com,app_10<--
Use of uninitialized value in concatenation (.) or string at ./rps_inv_lookup.pl line 120.
---><--

Указание моего собственногоПеременная цикла вместо того, чтобы полагаться на $ _, решает проблему.

Я просто наивен, когда использую $ _ в сочетании с API, написанным кем-то другим?Или это ошибка в этом модуле API?

Ответы [ 3 ]

9 голосов
/ 29 октября 2010

Это ошибка в API.Если вы используете $_ в функции, важно добавить

local($_);

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

Если вы можете ограничить yoursel версиями Perl> 5.9.1, то вы также можете сделать лексику $ _, что облегчает понимание, чем local с

my $_;

Ноэто сломается в более ранних версиях Perl.

От man perlvar:

Поскольку $_ является глобальной переменной, это может привести в некоторых случаях к нежелательным побочным эффектам.Начиная с perl 5.9.1, теперь вы можете использовать лексическую версию $_, объявив ее в файле или в блоке с помощью «my».Более того, объявление «our $_» восстанавливает глобальный $_ в текущей области.

6 голосов
/ 29 октября 2010

Я бы сказал, что это:

  1. нарушение передового опыта с вашей стороны (всегда используйте как локальную переменную как возможную область и избегайте использования $_ только из-за проблемы, с которой вы столкнулись)

  2. в сочетании с ошибка в API , вызванная тем же нарушением лучших практик, а также не локализации специальной переменной с local $_, как это запрещено perldoc perlvar .

В дополнение к perldoc, API нарушает Perl Best Practices (как в правилах книги Конвея):

Раздел 5.6. Локализация переменных пунктуации

Если вы вынуждены изменить пунктуацию, локализуйте ее.

Проблемы, описанные ранее в разделе «Локализация, также могут возникать всякий раз, когда вы вынуждены изменить значение в знаке препинания (часто в операциях ввода-вывода). Все переменные пунктуации имеют глобальный характер. было бы совершенно неявным поведением в большинстве других языков: буферизация вывода, нумерация строк ввода, окончания строк ввода и вывода, индексация массива и так далее.

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

Использование local - это самый чистый и надежный способ временно изменить значение глобальной переменной. Он всегда должен применяться в наименьшей возможной области видимости, чтобы минимизировать влияние любого «окружающего поведения», которое может контролировать переменная:

Вот также полная документация perldoc perlvar - поиск по слову "nasty_break" на веб-странице (я не смог найти прямую ссылку на странице, но она близка к началу страницы)

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

  1. открой мои $ fh, "<", "foo" или умри $!; </li>
  2. местный $ /; # включить режим локализованной стружки
  3. my $ content =;
  4. закрыть $ fh;

Но следующий код довольно плох:

  1. открой мои $ fh, "<", "foo" или умри $!; </li>
  2. undef $ /; # включить режим slurp
  3. my $ content =;
  4. закрыть $ fh;

, так как какой-то другой модуль, возможно, захочет читать данные из некоторого файла в по умолчанию «линейный режим», поэтому, если код мы только что представлен был выполнен, глобальное значение $ / теперь изменилось для любого другого кода, работающего внутри тот же интерпретатор Perl.

Обычно, когда переменная локализована Вы хотите убедиться, что это изменение влияет на кратчайший возможный объем. Так что, если вы уже не в какой-то короткий {} блок, вы должны создать его сам. Например:

  1. my $ content = '';
  2. открой мои $ fh, "<", "foo" или умри $!; </li>
  3. {
  4. местный $ /;
  5. $ content =;
  6. }
  7. закрыть $ fh;

Вот пример того, как ваши собственные код может сломаться:

  1. для (1..5) {
  2. nasty_break (); * * тысяча девяносто три
  3. печать "$ _";
  4. }
  5. sub nasty_break {
  6. $ _ = 5;
  7. # сделать что-то с $ _
  8. }

Вы, вероятно, ожидаете, что этот код печать:

  1. 1 2 3 4 5

но вместо этого вы получите:

  1. 5 5 5 5 5

Почему?Потому что nasty_break () изменяет $ _, не локализуя его в первую очередь.Исправление заключается в добавлении local ():

  1. local $ _ = 5;
2 голосов
/ 30 октября 2010
foreach (@rps_server_details) {
    @server_data = ();
    @server_data = split(/,/);
    @$esp_hosts = ();
    $filters{server_name} = $server_data[0];
    print "--->$_<--\n";
    {
        local *_;  # disconnects the remaining scope from the implicit 
                   # variables so you can clean up after the dirty api.
                   # NOTE: Submit a bug report against the offending module.
                   #       If you notice this across multiple api features
                   #       consider finding a different module for this task.
        $esp_hosts = $esp->get_hosts(fields => $fields, %filters) || die "$@";
    }
    print "--->$_<--\n";
...