Есть ли какой-нибудь способ сделать переменные типа $ a и $ b относительно строгих? - PullRequest
5 голосов
/ 30 сентября 2008

В свете комментария Майкла Кармана я решил переписать вопрос. Обратите внимание, что перед этим редактированием появляются 11 комментариев, и они подтверждают наблюдение Майкла о том, что я не написал вопрос таким образом, чтобы было понятно, о чем я спрашиваю. <Ч /> Вопрос: Каков стандартный - или самый чистый способ - подделать особый статус, который $a и $b имеют в отношении строгого, путем простого импорта модуля?

Прежде всего, некоторые настройки. Следующие работы:

#!/bin/perl
use strict;
print "\$a=$a\n";
print "\$b=$b\n";

Если я добавлю еще одну строку:

print "\$c=$c\n";

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

Если я закомментирую use strict;, все будет нормально. За пределами ограничений $a и $b в основном особенные, так как sort передает два значения для сравнения с этими именами.

my @reverse_order = sort { $b <=> $a } @unsorted;

Таким образом, основное функциональное различие относительно $a и $b - даже если Perl "знает их имена" - в том, что вам лучше знать это при сортировке или использовании некоторых из функции в List :: Util .

Только при использовании строгого * $a и $b становятся специальными переменными совершенно по-новому. Они являются единственными переменными, которые строгие значения будут передаваться без жалоб, что они не объявлены.

: Теперь мне нравится строгий режим, но мне кажется, что если TIMTOWTDI (существует более одного способа сделать это) - это правило № 1 в Perl, это не очень TIMTOWDI. Там написано, что $a и $b особенные и все. Если вы хотите использовать переменные, вам не нужно объявлять $a и $b ваши парни. Если вы хотите добавить три переменные, добавив $c, неожиданно появится совершенно другой способ сделать это.

Не важно, что при манипулировании хешами $k и $v может иметь больше смысла:

my %starts_upper_1_to_25 
    = skim { $k =~ m/^\p{IsUpper}/ && ( 1 <= $v && $v <= 25 ) } %my_hash
;`

Теперь я пользуюсь и люблю строгий. Но я просто хочу, чтобы $k и $v были видны skim для наиболее компактного синтаксиса. И я бы хотел, чтобы это было видно просто

use Hash::Helper qw<skim>;

Я не задаю этот вопрос, чтобы узнать, как его замаскировать. Мой «ответ» ниже должен дать вам знать, что я знаю достаточно Perl, чтобы быть опасным. Я спрашиваю, есть ли способ заставить строго принимать другие переменные, или каково решение cleanest . Ответ вполне может быть нет. Если это так, то это просто не похоже на TIMTOWTDI.

Ответы [ 13 ]

7 голосов
/ 30 сентября 2008

Другие упоминали, как использовать 'vars' и 'our' - я просто хотел добавить, что $ a и $ b являются особыми случаями, так как они используются внутри процедур сортировки. Вот примечание от строгих документов .pm:

Because of their special use by sort(), the variables $a and $b are 
exempted from this check.
4 голосов
/ 30 сентября 2008

$ a и $ b особенные, потому что они являются частью основного языка. Хотя я могу понять, почему вы можете сказать, что неспособность создавать собственные переменные с аналогичными характеристиками является анти-TIMTOWTDI, я бы сказал, что это не более, чем неспособность создавать новые базовые команды порядка «печати» или «печати». Сортировать'. (Вы можете определять подпрограммы в модулях, но это не делает их истинными ключевыми словами. Это эквивалентно использованию «нашего $ k», которое, как вы, похоже, говорите, не делает $ k достаточным для вас, как $ a.)

Для вставки имен в чужое пространство имен это должен быть рабочий пример Exporter:

package SpecialK;

use strict;

use base 'Exporter';
BEGIN {
  our @EXPORT = qw( $k );
}

our $k;

1;

Сохраните это в SpecialK.pm, и после этого «use SpecialK» сделает доступным $ k. Обратите внимание, что только «наши» переменные могут быть экспортированы, но не «my».

4 голосов
/ 30 сентября 2008

Если я правильно понимаю, что вы хотите:

use vars qw($a $b); # Pre-5.6

или

our ($a, $b); # 5.6 +

Вы можете прочитать об этом здесь .

2 голосов
/ 30 сентября 2008

Если я понимаю ваш вопрос, вы хотите написать модуль, который объявляет переменные в пространстве имен пользователя (поэтому они не обязаны) и который автоматически локализуется в обратных вызовах. Это верно?

Вы можете сделать это, объявив глобальные переменные и экспортировав их. (Хотя учтите, что экспортировать вещи, как правило, не рекомендуется).

package Foo;
use strict;
use warnings;

require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(*k *v hashmap);
our ($k, $v);

sub hashmap(&\%) {
    my $code = shift;
    my $hash = shift;

    while (local ($k, $v) = each %$hash) {
        $code->();
    }
}

Примечание: Экспорт состоит из *k и *v, а не $k и $v. Если вы не экспортируете весь typeglob, local in hashmap не будет работать правильно из пакета пользователя. Побочным эффектом этого является то, что все различных форм k и v (%k, @v и т. Д.) Объявляются и становятся псевдонимами. Для полного объяснения этого смотрите Таблицы символов в perlmod .

Тогда в вашем скрипте:

use Foo; # exports $k and $v

my %h = (a => 1, b => 2, c => 3);

hashmap { print "$k => $v\n" } %h;

__END__
c => 3
a => 1
b => 2
2 голосов
/ 30 сентября 2008

В Perl 5.6 и более поздних версиях вы можете использовать наши:

our ($k, $v);

Или вы можете придерживаться старых "use vars":

use vars qw($k $v);

Или вы могли бы просто придерживаться "моего", например ::100100

my %hash;
my ($k,$v);
while (<>) {
  /^KEY=(.*)/ and $k = $1 and next;
  /^VALUE=(.*)/ and $v = $1;
  $hash{$k} = $v;
  print "$k $v\n";
}

__END__
KEY=a
VALUE=1
KEY=b
VALUE=2

Создание глобального $ v на самом деле не является необходимым в приведенном выше примере, но, надеюсь, вы поймете идею (с другой стороны, $ k нужно ограничить пределами блока while).

В качестве альтернативы, вы можете использовать полные имена переменных:

$main::k="foo";
$main::v="bar";
%main::hash{$k}=$v;
1 голос
/ 01 октября 2008

$ a и $ b, однако, не являются обычными переменными и не могут быть легко реплицированы ни лексическими декларациями, ни явным экспортом, ни возни с таблицей символов. Например, используя отладчик в качестве оболочки:

  DB<1> @foo = sort { $b cmp $a } qw(foo bar baz wibble);

  DB<2> x @foo
0  'wibble'
1  'foo'
2  'baz'
3  'bar'
 DB<3> x $a
0  undef
  DB<4> x $b
0  undef

$ a и $ b существуют только в блоке, переданном sort (), впоследствии не существуют, и имеют область видимости таким образом, что любые дальнейшие вызовы sort не наступают на них.

Чтобы повторить это, вам, вероятно, нужно начать возиться с исходными фильтрами, чтобы включить предпочитаемую нотацию

my %starts_upper_1_to_25 
    = skim { $k =~ m/^\p{IsUpper}/ && ( 1 <= $v && $v <= 25 ) } %my_hash
;

в эффективно

my %starts_upper_1_to_25
    = map { my $k = $_; my $v = $my_hash{$v};
            $k =~ m/^\p{IsUpper}/ && ( 1 <= $v && $v <=> 25 ) } keys %my_hash
;

$ a и $ b такие же особенные, как $ _ и @_, и хотя в Perl 5 нет простого способа изменить эти имена, Perl 6 действительно исправляет это с помощью заданного ключевого слова. «данный» - это мусорный термин для поиска, но http://dev.perl.org/perl6/doc/design/syn/S03.html может быть хорошим началом.

1 голос
/ 30 сентября 2008

Я не уверен, что кто-то разъяснил это, но строгий не вносит белый список $ a и $ b только потому, что они являются действительно удобными именами переменных для использования в ваших собственных подпрограммах. $ a и $ b имеют особое значение для оператора сортировки. Это хорошо с точки зрения такого рода рутины, но плохой дизайн со стороны. :) Вы не должны использовать $ a и $ b в других контекстах, если вы используете.

1 голос
/ 30 сентября 2008

Это то, что вы после? .....

use strict;
use warnings;
use feature qw/say/;

sub hash_baz (&@) {
    my $code   = shift;  
    my $caller = caller;
    my %hash   = (); 
    use vars qw($k $v);

    no strict 'refs';
    local *{ $caller . '::k' } = \my $k;
    local *{ $caller . '::v' } = \my $v;

    while ( @_ ) {
        $k = shift;
        $v = shift;
        $hash{ $k } = $code->() || $v;
    }

    return %hash;
}

my %hash = ( 
    blue_cat   => 'blue', 
    purple_dog => 'purple', 
    ginger_cat => 'ginger', 
    purple_cat => 'purple' );

my %new_hash = hash_baz { uc $v if $k =~ m/purple/ } %hash;

say "@{[ %new_hash ]}";

# =>  purple_dog PURPLE ginger_cat ginger purple_cat PURPLE blue_cat blue
1 голос
/ 30 сентября 2008

Это сработало для меня:

package Special;
use base qw<Exporter>;
# use staging; -> commented out, my module for development
our $c;

our @EXPORT = qw<manip_c>;

sub import { 
    *{caller().'::c'} = *c;
    my $import_sub    = Exporter->can( 'import' );
    goto &$import_sub;
 } 

И он тоже проходит через c через строгий.

package main;
use feature 'say';
use strict;
use Special;
use strict;
say "In main: \$c=$c";

manip_c( 'f', sub {
    say "In anon sub: \$c=$c\n"; # In anon sub: $c=f
});

say "In main: \$c=$c";

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

1 голос
/ 30 сентября 2008

Звучит так, будто вы хотите творить магию, которую List::MoreUtils делает:

use strict;
my @a = (1, 2);
my @b = (3, 4);
my @x = pairwise { $a + $b } @a, @b;

Я бы предложил просто взглянуть на pairwise sub в List::MoreUtils source . Он использует некоторую хитрую таблицу символов, чтобы внедрить $a и $b в пространство имен вызывающего абонента, а затем локализовать их как раз внутри тела. Я думаю.

...