Данный
# package main;
our $f;
sub f{}
sub g {}
1;
Как я могу определить это $f
, но нет $g
, был объявлен? Экспромтом, я думал это *{main::g}{SCALAR}
могло бы быть не определено, но это - добросовестный СКАЛЯР касательно.
Фон: я хотел бы импортировать переменную в main::
, но карп или карканье, если та переменная уже объявляется.
EDIT, добавленная f
подпрограмма в ответ на первоначальный ответ @DVK.
ОТВЕТ (2010-07-27)
Это не легко, но это возможно.
eval
техника является самой портативной, работающий над жемчугом, более старым, чем 5,10. В более свежем жемчуге, самосозерцательных модулях как Devel::Peek
и B
может различить.
Я старался изо всех сил, даже до того, что попытался спросить eval STRING
, был ли $ main :: f
объявлен через ] наш
или мой
. (Это потребовало дублирования, закрытия и последующего восстановления STDERR
, чтобы сократить болтовню.) После того, как вы изменили пакеты, эти объявления больше не будут видны при временном переключении.
Приведенная ниже методика определяет, был ли $ f
объявлен через
use vars qw/ $f /;
Код ниже:
package MyModule;
use warnings;
use strict;
# using $f will confuse the compiler, generating
# warnings of 'Variable "%f" is not available'
# although we're going for $main::f
my $__f = "from MyModule";
my %IMPORT_OK = (
'$f' => [f => \$__f],
);
sub import {
my($pkg,@imports) = @_;
my $callpkg = caller;
die "I don't speak your dirty Pig-Latin"
if $callpkg !~ /\A\w+(::\w+)*\z/ ||
grep !/\A[\$@%]\w+\z/, @imports;
foreach my $name (@imports) {
my($sym,$ref) = @{ $IMPORT_OK{$name} || [] };
die "unknown import: $name" unless $sym;
open my $saverr, ">&", \*STDERR or die "dup STDERR: $!";
close STDERR;
my $declared = eval qq{
package $callpkg;
my(undef)=$name;
1;
};
open STDERR, ">&", $saverr or print "restore STDERR: $!";
die "${callpkg}::$sym already exists" if $declared;
{
no strict 'refs';
*{$callpkg . "::" . $sym} = $ref;
}
}
}
1;
Вы можете проверить определенную подпрограмму следующим образом:
say 'g() defined in main' if defined &{'main::g'};
К сожалению, тот же метод работает только с переменной пакета, если было присвоено значение:
our $f = 1;
say '$f defined with value in main' if defined ${'main::f'};
/ I3az /
В старых версиях perl (до 5.10) всегда что-то есть в скалярном слоте.
Похоже, что в новых версиях Perl старое поведение имитируется, когда вы попробуйте сделать * FOO {SCALAR}.
Вы можете использовать модуль самоанализа B для проверки скалярного слота, хотя:
# package main;
our $f;
sub f {}
sub g {}
use B;
use 5.010;
if ( ${ B::svref_2object(\*f)->SV } ) {
say "f: Thar be a scalar tharrr!";
}
if ( ${ B::svref_2object(\*g)->SV } ) {
say "g: Thar be a scalar tharrr!";
}
1;
РЕЗЮМЕ
На данный момент, после довольно обширного исследования, я твердо уверен, что в ситуация, когда запись в таблице символов с именем «X» была объявлена, но не назначена, невозможно в целом определить , какой из ссылочных типов в глобусе был фактически объявлен без использования глубокого исследования Devel :: вещи.
Другими словами, вы можете сказать только следующие две различные ситуации:
X не был объявлен вообще (запись в таблице символов не существует)
X был объявлен, и некоторые типы глобусов были фактически присвоены .
Во втором случае
Вы можете найти, КАКИЕ из типов глобусов были назначены, а какие не были
НО, вы не можете выяснить, какой из не назначенные глобальные типы были объявлены и неназначены, а не объявлены вообще.
Другими словами, для наш $ f = 1; наш @f;
; мы можем сказать, что $ main :: f
- скаляр;
но мы НЕ можем сказать, были ли объявлены @f
и % f
или нет - это вообще не отличается от нашего $ f = 1; наш% f;
.
Обратите внимание, что определения подпрограмм также следуют этому второму правилу, но объявление именованной подпрограммы автоматически присваивает ей значение (кодовый блок), поэтому вы никогда не можете иметь дополнительное имя в состоянии «объявлено, но не присвоено». (предостережение: может быть неверно для прототипов ??? без подсказки).
ОРИГИНАЛЬНЫЙ ОТВЕТ
Что ж, очень ограниченным (и ИМХО несколько хрупким) решением отличия скаляра от подпрограммы могло бы быть использование UNIVERSAL :: can:
use strict;
our $f;
sub g {};
foreach my $n ("f","g","h") {
# First off, check if we are in main:: namespace,
# and if we are, that we are a scalar
no strict "refs";
next unless exists $main::{$n} && *{"main::$n"};
use strict "refs";
# Now, we are a declared scalr, unless we are a executable subroutine:
print "Declared: \$$n\n" unless UNIVERSAL::can("main",$n)
}
Результат:
Declared: $f
Обратите внимание, что { SCALAR}
, похоже, не работает для отсеивания нескаляров в моем тестировании - он успешно прошел через @A
и % H
, если я объявил их и добавил в цикл .
ОБНОВЛЕНИЕ
Я попробовал подход Брайана Д Фоя из главы 8 «Освоения perl» и почему-то не смог заставить его работать со скалярами, хешами или массивами; но как отмечено ниже draegtun, это работает для подпрограмм или для переменных , которые уже были назначены на :
> perl5.8 -we '{use strict; use Data::Dumper;
our $f; sub g {}; our @A=(); sub B{}; our $B; our %H=();
foreach my $n ("f","g","h","STDOUT","A","H","B") {
no strict "refs";
next unless exists $main::{$n};
print "Exists: $n\n";
if ( defined ${$n}) { print "Defined scalar: $n\n"};
if ( defined @{$n}) { print "Defined ARRAY: $n\n"};
if ( defined %{$n}) { print "Defined HASH: $n\n"};
if ( defined &{$n}) { print "Defined SUB: $n\n"};
use strict "refs";}}'
Exists: f
Exists: g
Defined SUB: g <===== No other defined prints worked
Exists: STDOUT
Exists: A
Exists: H
Exists: B
Defined SUB: B <===== No other defined prints worked
Devel :: Peek, кажется, может различать использованные и неиспользуемые вещи в слоте SCALAR:
use strict;
use warnings;
use Devel::Peek;
our $f;
sub f { }
sub g { }
Dump(*f);
Dump(*g);
Результат:
SV = PVGV(0x187360c) at 0x182c0f4
REFCNT = 3
FLAGS = (MULTI,IN_PAD)
NAME = "f"
NAMELEN = 1
GvSTASH = 0x24a084 "main"
GP = 0x1874bd4
SV = 0x182c0a4
REFCNT = 1
IO = 0x0
FORM = 0x0
AV = 0x0
HV = 0x0
CV = 0x24a234
CVGEN = 0x0
LINE = 6
FILE = "c:\temp\foo.pl"
FLAGS = 0xa
EGV = 0x182c0f4 "f"
SV = PVGV(0x187362c) at 0x18514dc
REFCNT = 2
FLAGS = (MULTI,IN_PAD)
NAME = "g"
NAMELEN = 1
GvSTASH = 0x24a084 "main"
GP = 0x1874cbc
SV = 0x0
REFCNT = 1
IO = 0x0
FORM = 0x0
AV = 0x0
HV = 0x0
CV = 0x1865234
CVGEN = 0x0
LINE = 8
FILE = "c:\temp\foo.pl"
FLAGS = 0xa
EGV = 0x18514dc "g"
Интересующие строки находятся под GP =
раздел, а именно SV, AV, HV и CV (скаляр, массив, хэш и код соответственно). Обратите внимание, что дамп * g
показывает SV = 0x0
. К сожалению, не существует программного способа получить эту информацию. Тупой инструментальный подход - захватить вывод Dump ()
и проанализировать его.