perl tricks

Tags:

这是我在知乎上的答题。

// 操作符

$a = $b // $c;
# $a = $b, 如果$b 为undef, $a = $c

常常我会这么写来给变量定义一个默认值

$some_var //= 0;

... yada yada

当我们定义一个函数而打算在未来实现它

sub unimplemented {
    ...
}

三个dot 这个操作符表示这里还有待实现,程序运行不会报错,但如果使用到这个未定义的函数,则会抛出'unimplement'的异常 这个操作符有个名字 yada yada 是来自日语的词汇,释义为等等,之类的意思

-> 和 autobox

-> 操作符有个比较有意思的一个地方,除了我们常用的

$hash->{key}
$list->[0]
$obj->method()

之外,还有一个神奇的功能来实现autobox,也就是所谓的·一切皆对象·

my $len = sub { length(shift) };
"hello"->$len();

如果想要没有$的autobox, "hello"->len(),可以看看CPAN 上的autobox::Core

s///r

是否遇到希望用s///来得到想要的字符串而不希望改变原来的变量?

$x = "I like dogs.";
$y = $x =~ s/dogs/cats/r;
print "$x $y\n"; # prints "I like dogs. I like cats."

here doc 和 inline file

here doc 可以像下面这样定义一个长的字符串

my $str = <<__;
some string here
bla blah
__

inline file 可以像下面这样在程序内部定义一个file,就像操作文件句柄一样

while( <DATA> ) {
    print;
}
__DATA__
a b c d
1 2 3 4

flip-flop

.. 除了在列表上下文作为范围操作符使用

'a'..'z' # ('a', 'b', ... 'z')

在标量上下文也有特别的用法,称作flip-flop操作符,行为就像一个状态触发器。

while(<$fh>) { next if 1..100; # skip 1 ~ 100 line ... }

autovivification

这个应该是perl所特有的一种用法 使得我可以不必依次定义一个数据的每一级类型,而直接对其进行任意深度的赋值

my $hash = {};
$hash->{a}->{b}->{c} = 1;

方便之外,也可能会带来麻烦

my $hash = {};
if ( exists $hash->{a} ) {
    print 'exists';     # no
}
if ( exists $hash->{a}->{b} ) {
    print 'exists';     # no
}
if ( exists $hash->{a} ) {
    print 'exists now'  # yes
    }

我们可以使用no autovivification 来关闭这一特性

quote word list: qw

这个非常常用,在写一个字符串数组的时候,不用麻烦的大引号了逗号了

qw( item1 item2 item3 )  => ('item1', 'item2', 'item3')

x

'a' x 4 # 'aaaa'

对数组同样有效

(1,2,3) x 3 # (1,2,3,1,2,3,1,2,3)

label

可以在任意位置定义标签,通过goto 进行跳转。 当然很多人都不推荐在程序大量使用goto, 但是perl中定义标签还有一个非常有用的功能,就是循环中使用next 或 last + 标签来进行跨层使用。

LABEL:
for ( @list_A) {
    for ( @list_B ) {
       ...;
       next LABEL;
    }

}

@{[...]}

由于perl的sigil的存在,使得perl在print 的时候很容易内插变量,比如在c 中

printf("my name is %s, i'm %d", name, age)

而在perl 中就可以这么写

print "my name is $name, i'm $age";

@{[...]} 更是极大的扩展了这一功能,你可以在字符串中内插表达式

my $x = 1;
my $y = 2;
print "$x + $y = @{[ $x + $y ]}"

($x, $y) = ($y, $x );

python 中的 a, b = b, a

perl 变量 my our local state

Tags:

perl 变量类型与作用域

根据作用域的不同来区分,perl有两种变量类型:

  • 词法变量(Lexical Variable) -- 词法作用域,只在当前词法作用域内可见。

  • 包变量(Package Variable) -- 全局变量,在任何位置可见

有三种创建变量的方式:

  • my -- 创建一个词法变量

  • our -- 创建一个词法变量,但实际上是包变量的别名,或者说是伪装成词法变量的包变量

  • 直接使用 -- 不加任何声明的使用一个变量,将创建一个包变量

下面我们详细说一下这几种方式

直接使用 -- 包变量

$foo = 1;

在perl早期的时候,大家都是这么定义,使用变量的。

这么定义变量$foo将会是全局变量,也就是在程序的任何位置都能访问到它。

这样当随着代码规模的增长,会引起一个严重的问题,变量污染,比如:

$foo = 1; # $foo -> 1
f();
          # $foo -> 2
sub f {
   $foo = 2;
}

如果不小心命名,则很容易在某个意想不到的位置,变量$foo被赋值,出现bug。

所以 perl5 引进了strict 和warnings 两个pragma,在代码上加上use strict后,所有没有

直接声明而使用的变量都会报错。

所以现在这种用法已经废弃了,在一些教程书中会这么开始教学,但这种用法在实际代码中是绝对不建议再使用的。

取而代之的是 my

my -- 词法变量

my $foo = 1;

my 的出现,极大的解决了变量污染的问题,因为my所声明的变量是 -- 局部变量,词法作用域。

局部变量的概念,比较好理解,比如:

my $foo = 1; # $foo -> 1
f();
             # $foo -> 1
sub f {
   my $foo = 2 # $foo -> 2
}

在函数f中的$foo 之在局部范围内起作用,出了函数之后,就不再起作用,所以也就不会影响到外部的$foo

需要理解的是这里的词法作用域,词法作用域限制了局部变量的名字生存的范围,这里一个词法作用域,基本上就是一个BLOCK,也就是一个花括号阔住的范围。

所以,只要有一个BLOCK就会建立一个词法作用域,比如:

my $foo = 1; # $foo -> 1
f();
             # $foo -> 1
sub f {
    my $foo = 2 # $foo -> 2
    if ( 1 ) {
        my $foo = 3 # $foo -> 3
    }
}

这里在函数f内部的if语句中,建立一个新的词法作用域,同样对这里的$foo赋值不会影响到外部的$foo

my基本上已经够用了,然而由于之前提到的strict 和warnings ,导致不能使用全局变量。

因此出现了our

our -- 伪装成词法变量的包变量

our $foo = 1;

our 的出现解决了不能使用全局变量的问题,our所声明的变量是全局变量的词法作用域的别名。

意思就是,这里$foo是$main::foo的别名,如果是在模块MyModule中声明的话,就是$MyModule::foo.

但是它同时又是词法作用域的,所以在词法作用域范围外无法访问到它,只能通过模块变量名$MyModule::foo来访问

可以说 our 所声明的变量是伪装成词法变量的全局变量。

举个例子:

package A;

{
    our $foo = 1;
    print $foo;     # 1
}
print $foo;         # error
print $A::foo;      # 1

package B;
print $A::foo;      # 1

这里在花括号之外将无法访问到$foo, 但能通过模块变量名来访问 $A::foo

在模块B中,也能通过$A::foo来访问模块A所定义的变量。

local -- 包变量的临时赋值

local $foo = 1;

local 并不创建一个新的变量,而是对已有的包变量临时赋予一个值,在退出当前scope后将原来的值还回去。

$foo = 1;                 # $foo -> 1
{
    local $foo = 2;       # $foo -> 2  将原先的$foo藏起来,赋予一个临时的值
}
$foo                      # $foo -> 1  将藏起来的值还回去

看起来有点像my,local确实是在my出现以前的解决方案,在my出来之后,使用的情形也就不多了。

比较有意思的是,local是动态作用域,什么意思呢? 我们比较一下下面两个代码:

my $foo = 1;
{
    my $foo = 2;
    f();
}
sub f {
    print "foo is $foo";
}

结果是

foo is 1

因为词法作用域的$foo对函数f也是不可见的,函数f看到的是在最外层的$foo,而local就不同了

$foo = 1;
{
    local $foo = 2;
    f();
}
sub f {
    print "foo is $foo";
}

结果是

foo is 2

这就是动态作用域,对一般的代码来说,local的适用范围并不广,因为本身全局变量就是不提倡使用的,而local只对全局变量起作用,

可能有些时候你需要对内建的全局变量比如 $/, $_ 临时赋值,这时候会用到local。

state -- 词法变量,静态变量

state $foo = 1;

state比较好理解,它就像c中的静态变量一样,只在第一次调用时候创建该变量,之后都是操作的同一个变量

state也是词法变量,所以只在定义该变量的词法作用域中有效,举个例子:

sub count {
    state $counter = 0;
    $counter++;
    return $counter;
}
say count();
say count();
say count();

总结

因为perl的向下兼容性做的非常好,所以perl也抗了很多历史包袱,比如这里提到的不声明直接使用的全局变量。

现在提倡的用法是变量都用my声明,在需要全局包变量的情况下,用our声明,所有代码前都要加上use strict 和use warnings

这两个pragma, 在某些特定的时候会需要local 和 static 。