Как отключить оптимизацию для неиспользуемых переменных в текущей области? - PullRequest
2 голосов
/ 08 апреля 2020

Например, у меня есть код:

my $x =  77;

sub test {
    $DB::single =  1;
    1; 
}
test();

Если я запускаю этот скрипт с флагом -d: perl -d test.pl, выполнение этого скрипта будет остановлено на точке останова (строка 5). С точки останова $x переменная видна. Если я попытаюсь просмотреть через отладчик его значение, оно будет неопределенным. Если я изменю свою программу следующим образом:

sub test {
    print $x;
    $DB::single =  1;
    1; 
}
test();

, тогда будет видно $x.

Есть ли какая-либо опция отладчика perl, которая позволит отключить оптимизацию для неиспользуемых переменных в текущем сфера

Ответы [ 2 ]

5 голосов
/ 08 апреля 2020

Нет.

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

В случае, когда подпрограмма видит $x, потому что она использует $x, это потому что саб захватил $x. Подпрограммы захватывают только те переменные, которые им нужны. Захват без необходимости может иметь очень пагубные последствия (потому что деструкторы не будут вызываться, когда ожидается).

Невозможно сказать Perl делать иначе, кроме как по переменным.


Прежде всего, код, который вы разместили, не демонстрирует описанную вами проблему.

$ perl -d a.pl

Loading DB routines from perl5db.pl version 1.55
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(a.pl:1): my $x =  77;
  DB<1> r
main::test(a.pl:5):         1;
  DB<1> x $x
0  77
  DB<2> q

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

Foo.pm:

package Foo;

my $x = 77;

sub test {
    $DB::single =  1;
    1;
}

1;
$ perl -d -I. -e'use Foo; Foo::test()'

Loading DB routines from perl5db.pl version 1.55
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(-e:1):   use Foo; Foo::test()
  DB<1> r
Foo::test(Foo.pm:7):        1;
  DB<1> x $x
0  undef
  DB<2> q

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


Скажите, что у вас было

{
   my $x = 77;
}

CORE::say $x;

Это не работает, верно? Это не из-за оптимизации - $x был создан, и ему было присвоено 77, - это потому, что $x прекратил свое существование, когда вышла лексическая область, которая его заключала.

Теперь давайте рассмотрим следующее:

{
   my $x = 77;
   sub get_x { $x }
}

CORE::say get_x();

Это как-то печатает 77. Как это возможно? Я только что объяснил, что $x прекратил свое существование до достижения последней строки. Ну, он бы прекратил свое существование, если бы get_x не захватил его. Мы говорим, что get_x является замыканием . $x продолжает существовать, но только в закрытии.

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


Какое отношение это имеет к вашему вопросу? Ну, блоки (кудри) не единственные вещи, которые создают лексические рамки. Файлы, загруженные Perl, выполняются в лексической области fre sh.

Другими словами, вышеприведенный тест на неудачу примерно эквивалентен

BEGIN {
    package Foo;

    my $x = 77;

    sub test {
        $DB::single =  1;
        1;
    }

    $INC{"Foo.pm"} = 1;
}

use Foo;
Foo::test();

И сводится к

{
    my $x = 77;

    sub test {
        $DB::single =  1;
        1;
    }
}

test();     

Учитывая вышесказанное, эффективно задается следующий вопрос:

Есть ли способ сообщить Perl, что переменные захвата подчиненных элементов не нужны? не используете?

Ответ - нет.

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

Нет способа указать Perl делать иначе, кроме как по переменной за переменной основе (например, добавив $x if 0; к подпункту).


Приложение: Вы можете столкнуться с подобными проблемами с eval EXPR.

$ perl -e'
    my $x = 77;
    sub test {
        return eval($_[0]);
    }

    printf "[%s]\n", test(q{$x});
'
[77]

$ perl -e'
    {
        my $x = 77;
        sub test {
            return eval($_[0]);
        }
    }

    printf "[%s]\n", test(q{$x});
'
[]

$ perl -e'
    {
        my $x = 77;
        sub test {
            $x if 0;
            return eval($_[0]);
        }
    }

    printf "[%s]\n", test(q{$x});
'
[77]

Приложение: Захваты очень полезный! Они делают обратные вызовы бризом.

sub any {
   my $callback = shift;
   for (@_) {
      return 0 if $callback->();
   }

   return 1;
}

my %bad = map { $_ => 1 } qw( foo bar );

die if any(sub { $bad{$_} }, @args);  # Captures %bad
0 голосов
/ 09 апреля 2020

thx to @ ikegami

Здесь я перефразирую его ответ вкратце:

{                    # 1. start scope 
    my $x = 77;      # 2. assign $x value

    sub test {
        $DB::single =  1;
        1;           # 5. execution stopped when $x does not exists anymore
    }
}                    # 3. scope is finished, $x is destroyed

test();              # 4. call test sub

Здесь $ x находится в своей области видимости. Когда я остановился на test, $x больше не существует.

Так что это не оптимизация, это круг жизни, который нельзя отключить =)

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