是否有某种方法可以像严格一样制作$ a和$ b等变量?

时间:2008-09-29 20:16:38

标签: perl timtowtdi

根据迈克尔卡曼的评论,我决定改写这个问题。请注意,在此编辑之前会出现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中的一些功能。

只有当您使用strict时,$a$b才会以全新的方式成为特殊变量。它们是唯一会在没有抱怨它们未被声明的情况下传递严格的变量。

现在,我喜欢严格,但令我感到震惊的是,如果TIMTOWTDI(有多种方法可以做到这一点)是Perl中的规则#1,那么这不是非常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$vskim可见,以获得最紧凑的语法。我希望它只是通过

可见
use Hash::Helper qw<skim>;

我不是在问这个问题,而是知道如何使它变黑。下面我的“回答”,应该让你知道我知道Perl是危险的。我问是否有办法严格接受其他变量,或者什么是最干净的解决方案。答案很可能是否定的。如果是这种情况,它似乎不是非常TIMTOWTDI。

13 个答案:

答案 0 :(得分:7)

其他人提到了如何“使用变量”和“我们的” - 我只是想补充说$ a和$ b是特殊情况,因为它们在排序例程内部使用。这是来自strict.pm docs的说明:

Because of their special use by sort(), the variables $a and $b are 
exempted from this check.

答案 1 :(得分:4)

如果我理解正确,你想要的是:

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

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

您可以阅读here

答案 2 :(得分:4)

$ a和$ b是特殊的,因为它们是核心语言的一部分。虽然我可以看到为什么你可能会说无法创建你自己的类似特殊变量是反TIMTOWTDI,但我会说它不能再按照'print'或''顺序创建新的基本命令了。分类'。 (你可以在模块中定义子元素,但这并不能使它们成为真正的关键字。它相当于使用'我们的$ k',你似乎在说这对你来说不会像$ a那样足够$。)< / p>

为了将名称推送到别人的名称空间,这应该是Exporter的一个工作示例:

package SpecialK;

use strict;

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

our $k;

1;

将此保存到SpecialK.pm并且“使用SpecialK”然后应该为您提供$ k。请注意,只能导出“我们的”变量,而不能导出“我的”。

答案 3 :(得分:2)

在Perl 5.6及更高版本中,您可以使用我们的:

our ($k, $v);

或者你可以坚持使用较旧的“使用大炮”:

use vars qw($k $v);

或者你可能只是坚持使用“我的”,例如:

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;

答案 4 :(得分:2)

如果我理解你的问题,你想编写一个模块,在用户的命名空间中声明变量(所以他们不必这样做),并在回调中自动进行本地化。是吗?

您可以通过声明全局变量并导出它们来完成此操作。 (虽然请注意,在没有被要求的情况下导出东西通常被认为是不好的形式。)

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中的hashmap将无法从用户的包中正常工作。这样做的副作用是{em>所有各种形式的kv%k@v等)被声明并且别名。有关此内容的完整说明,请参阅Symbol Tables in 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

答案 5 :(得分:1)

$a$b只是全局变量。只需声明$k$v

即可实现类似效果
use strict;
our ($k, $v);

(在这种情况下,$k$v不是全局变量,而是包变量的词法范围别名。但如果你不跨越边界,它就足够了。)

答案 6 :(得分:1)

听起来你想做List::MoreUtils所做的那种魔术:

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

我建议您查看pairwisein the List::MoreUtils source。它使用一些聪明的符号表摆动将$a$b注入调用者的命名空间,然后将它们本地化到子体内。我想。

答案 7 :(得分:1)

这对我有用:

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";

是的,我用“严格使用”方式将我的模块括起来是有点愚蠢,但我不知道内部结构,这会解决潜在的排序问题。

答案 8 :(得分:1)

这是你的事吗?.....

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

答案 9 :(得分:1)

我不确定是否有人澄清了这一点,但严格并未将$ a和$ b列入白名单,因为它们是您在自己的例程中使用的非常方便的变量名称。 $ a和$ b对sort运算符有特殊含义。从这种分类程序的角度来看这是好的,但是来自外部的那种糟糕的设计。 :)如果你是的话,你不应该在其他环境中使用$ a和$ b。

答案 10 :(得分:1)

但是,$ a和$ b不是正常变量,并且不能通过词法声明或显式导出或使用符号表进行混乱来轻松复制。例如,将调试器用作shell:

  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可能是一个很好的起点。

答案 11 :(得分:0)

模块建议使用导出与use vars没有什么不同。 但是use vars需要在每个使用$ a-like变量的包中完成。 并且our()需要在每个外部范围内完成。

请注意,使用$$ - prototyped sub可以避免使用$ a和$ b进行排序:

sub lccmp($$) { lc($_[0]) cmp lc($_[1]) }
print join ' ', sort lccmp
   qw/I met this guy and he looked like he might have been a hat-check clerk/;

当在排序调用之外的其他包中使用比较例程时,这是必不可少的。

答案 12 :(得分:-3)

编辑 - 这实际上是不正确,请参阅评论。离开这里让其他人有机会从我的错误中学习:)


哦,你问是否有一种方法让模块在CALLER的命名空间中声明$ k和$ v?您可以使用Exporter将变量推送到调用者:

use strict;

package Test; 
use Exporter;

my @ISA = qw/Exporter/; 
my $c = 3; 
my @EXPORT = qw/$c/; 

package main; 
print $c;