这是我在知乎上的答题。
// 操作符
$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
...
}$fh>
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 变量类型与作用域
根据作用域的不同来区分,perl有两种变量类型:
有三种创建变量的方式:
下面我们详细说一下这几种方式
直接使用 -- 包变量
$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 。