5

根据迈克尔卡曼的评论,我决定重写这个问题。请注意,在此编辑之前出现了 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;

因此,关于and的主要功能区别——即使 Perl“知道它们的名字”——是你最好在排序时知道这一点,或者使用List::Util中的一些函数。$a$b

只有当您使用严格时,才会$a$b全新的方式成为特殊变量。它们是 strict 唯一会通过而不抱怨它们没有被声明的变量。

:现在,我喜欢严格,但令我震惊的是,如果 TIMTOWDI(有不止一种方法可以做到)是 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并且对于最紧凑的语法$v是可见的。skim我希望它可以简单地通过

use Hash::Helper qw<skim>;

我问这个问题不是为了知道如何对它施以魔法。我在下面的“答案”应该让你知道我知道的 Perl 足够危险。我在问是否有办法严格接受其他变量,或者什么是最干净的解决方案。答案很可能是否定的。如果是这样的话,它根本就不是很TIMTOWTDI。

4

13 回答 13

7

其他人提到了如何“使用 vars”和“我们的”——我只是想补充一点,$a 和 $b 是特殊情况,因为它们在内部由排序例程使用。这是来自 strict.pm 文档的注释:

Because of their special use by sort(), the variables $a and $b are 
exempted from this check.
于 2008-09-29T20:34:08.147 回答
4

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

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

或者

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

你可以在这里阅读。

于 2008-09-29T20:25:09.090 回答
4

$a 和 $b 很特别,因为它们是核心语言的一部分。虽然我可以理解为什么您可能会说无法创建自己的类似特殊变量是反 TIMTOWTDI,但我会说这只不过是无法按“打印”或“的顺序创建新的基本命令”种类'。(您可以在模块中定义 subs,但这并不能使它们成为真正的关键字。这相当于使用 'our $k',您似乎是在说 $k 对您来说不够像 $a。)

要将名称推送到其他人的命名空间中,这应该是 Exporter 的一个工作示例:

package SpecialK;

use strict;

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

our $k;

1;

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

于 2008-09-29T22:26:49.563 回答
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;
于 2008-09-29T20:35:48.800 回答
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->();
    }
}

注意:导出是*kand *v,而不是$kand $v。如果您不导出整个 typeglob,则localinhashmap将无法从用户的包中正常工作。这样做的副作用是所有各种形式的kand 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
于 2008-09-30T16:00:07.563 回答
1

$a并且$b只是全局变量。您可以通过简单地声明$kand来实现类似的效果$v

use strict;
our ($k, $v);

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

于 2008-09-29T20:24:41.993 回答
1

听起来你想做这样的魔法List::MoreUtils

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

我建议只查看sourcepairwise的sub 。它使用一些巧妙的符号表摆弄来注入并进入调用者的命名空间,然后将它们本地化到子主体内。我认为。List::MoreUtils$a$b

于 2008-09-29T22:39:45.507 回答
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";

是的,我用“use strict”括起来我的模块有点愚蠢,但我不知道内部原理,这会解决潜在的排序问题。

于 2008-09-29T22:40:17.063 回答
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
于 2008-09-29T23:36:50.220 回答
1

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

于 2008-09-30T13:04:59.280 回答
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可能是一个不错的起点。

于 2008-10-01T00:26:18.533 回答
0

建议使用 export 的模块实际上与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/;

当在与排序调用不同的包中使用比较例程时,这是必不可少的。

于 2008-10-02T06:44:27.040 回答
-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;
于 2008-09-29T20:53:58.927 回答