Это недостаток дизайна, что сабвуферы Perl не имеют лексической области? - PullRequest
12 голосов
/ 23 сентября 2011
{
  sub a {
    print 1;
  }
}
a;

Ошибка, не так ли?

a не должна быть доступна извне.

Работает ли она в Perl 6 *?

* ИзвинитеЯ еще не установил его.

Ответы [ 7 ]

30 голосов
/ 23 сентября 2011

Вы спрашиваете, почему саб виден за пределами блока?Если это так, то это потому, что ключевое слово sub времени компиляции помещает sub в пространство имен main (если только вы не используете ключевое слово package для создания нового пространства имен).Вы можете попробовать что-то вроде

{
  my $a = sub {
    print 1;
  };
  $a->(); # works
}
$a->(); # fails

В этом случае ключевое слово sub не создает подпрограмму и не помещает ее в пространство имен main, а вместо этого создает анонимную подпрограмму и сохраняет ее в лексической областипеременная.Когда переменная выходит из области видимости, она больше не доступна (обычно).

Чтобы узнать больше, ознакомьтесь с perldoc perlsub

Кроме того, знаете ли вы, что выможете проверить, как Perl-анализатор видит ваш код?Запустите perl с флагом -MO=Deparse, как в perl -MO=Deparse yourscript.pl.Ваш исходный код анализируется как:

sub a {
    print 1;
}
{;};
a ;

Сначала скомпилируется подпрограмма, затем запускается блок без кода, затем вызывается a.

Для моего примера в Perl6 см .: Успех , Отказ .Обратите внимание, что в Perl 6 разыменование составляет ., а не ->.

Редактировать: Я добавил еще один ответ о новой экспериментальной поддержке лексических подпрограмм, ожидаемой для Perl 5.18.

17 голосов
/ 23 сентября 2011

В Perl 6 подпрограммы действительно имеют лексическую область действия, поэтому код выдает ошибку (как уже отмечали несколько человек).

Это имеет несколько интересных последствий:

  • вложенные именованные подпрограммы работают как правильные замыкания (см. Также: предупреждение «не останется общим» в perl 5)
  • импорт сабвуферов из модулей работает в лексических областях
  • встроенные функции предоставляются во внешней лексической области («настройка») вокруг программы, поэтому переопределение так же просто, как объявление или импорт функции с тем же именем
  • , поскольку лекспады являются неизменяемыми во время выполнения, компилятор может обнаруживать вызовы неизвестных подпрограмм во время компиляции (это уже делает niecza, Rakudo только в ветви "оптимизатора").
14 голосов
/ 23 сентября 2011

Подпрограммы имеют пакетную, а не блочную область.

#!/usr/bin/perl
use strict;
use warnings;

package A;
sub a {
    print 1, "\n";
}
a();
1;

package B;
sub a {
    print 2, "\n";
}
a();
1;
11 голосов
/ 24 сентября 2011

Именованные подпрограммы в Perl создаются как глобальные имена. Другие ответы показали, как создавать лексические подпрограммы, назначая анонимную подпрограмму лексической переменной. Другой вариант - использовать переменную local для создания динамической области действия.

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

use strict;
use warnings;
sub test_sub {
    print "in test_sub\n";
    temp_sub();
}

{
    local *temp_sub = sub {
        print "in temp_sub\n";
    };
    temp_sub();
    test_sub();
}
test_sub();

Это должно напечатать

in temp_sub
in test_sub
in temp_sub
in test_sub
Undefined subroutine &main::temp_sub called at ...
7 голосов
/ 13 октября 2012

На риск еще одного ругательства @tchrist, я добавляю еще один ответ для полноты.Ожидается, что пока еще не выпущенный Perl 5.18 будет включать лексические подпрограммы в качестве экспериментальной функции.

Вот ссылка на соответствующую документацию. Опять же, это очень экспериментально, это не должноиспользоваться для производственного кода по двум причинам:

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

Так что поиграйте с этимновая игрушка, если хотите, но вас предупредили!

7 голосов
/ 23 сентября 2011

Если вы видите код, скомпилированный, запущенный и напечатавший «1», значит, у вас нет ошибки.

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

my $x = ...;
sub f { $x }
5 голосов
/ 26 сентября 2011

Да, я думаю, что это конструктивный недостаток - точнее, первоначальный выбор использования динамического определения объема, а не лексического определения объема, сделанный в Perl, что, естественно, приводит к такому поведению. Но не все дизайнеры и пользователи языка согласятся. Поэтому на вопрос, который вы задаете, нет четкого ответа.

Лексическая область видимости была добавлена ​​в Perl 5, но в качестве дополнительной функции вам всегда нужно указывать это конкретно. С этим выбором дизайна я полностью согласен: важна обратная совместимость.

...