个人最新博客地址http://www.skylway.com/
分类: Python/Ruby
2012-11-10 13:41:00
从数学角度来看,操作符是特殊形式的普通函数;从语言学的角度来看,操作符是不规则动词,而各种数据类型则是名词。
根据带参数的多少,操作符可分为一元操作符(unary)、二元操作符(binary)、三元操作符(ternary)以及多元操作符(主要指list operators,可跟任何多参数)。
操作符的性质包括元数(arity)、优先级(precedence)和附着性(associativity)。附着性包括左附、右附和不附着。
操作符一览按照优先级从高到低,Perl的操作符列表如下:
Name |
Associativity |
Arity |
Precedence Class |
Terms, and list operators(leftward) (项与左附列表运算符) |
None |
0 |
Term指变量、引号运算符、 括号运算符(()[] {})等 |
The Arrow Operator (箭头操作符) |
Left |
2 |
-> |
Autoincrement and Autodecrement (自增自减) |
None |
1 |
++ -- |
Exponentiation (乘方) |
Right |
2 |
** |
Ideographic Unary Operators (表意一元操作符) |
Right |
1 |
! ~ / and unary + and - |
Binding Operators (捆绑操作符) |
Left |
2 |
=~ !~ |
Multiplicative Operators (乘操作符) |
Left |
2 |
* / % x |
Additive Operators (加操作符) |
Left |
2 |
+ - . |
Shift Operators (移位操作符) |
Left |
2 |
<< >> |
Name Unary and File Test Operators (有名一元和文件测试操作符) |
Right |
0,1 |
-f my do rmdir return等 |
Relational Operators (关系运算符) |
None |
2 |
< > <= >= lt gt le ge |
Equality Operators (等号操作符) |
None |
2 |
== != <=> eq ne cmp |
Bitwise Operator (位操作符) |
Left |
2 |
& |
Bitwise Operators (位操作符) |
Left |
2 |
| ^ |
Logical Operator (逻辑操作符) |
Left |
2 |
&& |
Logical Operator (逻辑操作符) |
Left |
2 |
|| |
Range Operators (范围操作符) |
None |
2 |
.. … |
Conditional Operator (条件操作符) |
Right |
3 |
?: |
Assignment Operators (赋值操作符) |
Right |
2 |
= += -= *= and so on |
Comma Operators (逗号操作符) |
Left |
2 |
, => |
List Operators(Rightward) (右附列表操作符) |
Left |
0+ |
List operators (rightward) |
Logical not (逻辑非) |
Right |
1 |
not |
Logical and (逻辑与) |
Left |
2 |
and |
Logical or xor (逻辑或与异或) |
Left |
2 |
or xor |
项在Perl的优先级最高。项包括变量、引号操作符、在各种括号内的表达式和参数用括号括起来的函数。注意chdir($foo) *20 表示 (chdir $foo)* 20, chdir +($foo) * 20 表示 chdir ($foo * 20)。另外要注意括号的附着性,比如print $foo, exit可能不是你所需要的,但是print($foo), exit却达到目的!另外: print ($foo*255)+1, “/n”也不能达到打印($foo*255)+1的目的!所以一定要小心!如果有疑惑,可以加括号消除歧义。
3.3.2 箭头操作符像C/C++一样,Perl的箭头操作符(->)指反引(dereference):
右边是[…]时,左边必须是数组引用;右边是{…}时,左边必须是Hash引用;左边是(…)时,左边必须是子程序引用或者一个对象(a bless reference)或类名(a package name)。比如,
$aref->[42] #an array dereference
$href->{“corned beef”} #a hash dereferenceu
$sref->(1,2,3) #a subroutine dereference
$yogi = Bear->new(“Yogi”); #a class method call
$yogi->swipe($picnic); #an object method call
3.3.3 自增自减Perl的++和--操作符与C的操作一致。但是Perl的++却有一个特殊的功能:当它对/^[a-zA-Z]*[0-9]*$/形式的字符串操作时,它是带进位的字符串自增,比如:
print ++($foo = ‘99’); #prints ‘100’
print ++($foo = ‘a9’); #prints ‘b0’
print ++($foo = ‘Az’); #prints ‘Ba’
print ++($foo = ‘zz’); #prints ‘aaa’
3.3.4 乘方C中没有乘方运算符,Perl的乘方运算符(**)是从FORTRAN中借鉴来的,使用C的pow(3)函数来实现。注意乘方运算符是右附的,所以-2**4是指-(2**4)而不是(-2)**4。
3.3.5 表意一元操作符这些操作符基本都与求反相关:
!是逻辑求反,相当于not, 但not的优先级更低
- 如果操作数是数值则是算术负。注意如果操作数是一个标识符,将返回一个负号与标识符的连接值!(如– “abc” 是”-abc”, - “-abc”是”+abc”)
~ 位求反。注意此操作是不可移植的。 ~123在32位机器与64位机器上结果是不同的!
+ 正号无论对数值与字符串都不起作用。但是正号有特别的作用是防止括号与函数名连接。如print +($foo*255)+1, “/n”;
/ 对任何跟随的操作数生成一个引用
3.3.6 捆绑操作符这些操作将一个字符表达式与模式匹配、替换或转换捆绑,否则这些操作将作用在$_变量上。
注意$_ =~ $pat等价于$_ =~ /$pat/,$string !~ /pattern/等价于 not $string =~ /pattern/。如果操作作用在替换上,将返回替换的次数,一般返回是否匹配成功。
3.3.7 乘操作符*(乘)、/(除)、%(求模)的基本行为与C一致。关于此类操作符需说明以下几点:
l /默认是进行浮点计算,除非use integer(这一点与C不同!)。即$i=23; print $i/2不是打印12,而是11.5。
l %将在求模前将操作数转换为整数。
l x是Perl特有的重复操作符!非常实用!如print ‘-‘ x 80;将打印一行-;@ones =(5) x @one将所有@ones的元素设为5。
3.3.8 加操作符注意事项:
l Perl的+(加)-(减)操作符若作用在字符串上,会将字符串转化为数值!
l Perl有另外的.操作符进行字符串连接。Perl不会在字符串之间加空格。如果有多个字符串需要连接,可以使用join函数。
l Perl的字符串连接操作符类似AWK的空格。其+-将字符串转化为数值也是从AWK借鉴来的。
3.3.9 移位操作符左移(<<)右移(>>)操作符行为与C一致,操作数必须为整数,注意结果依赖于机器表示整数的位数。
3.3.9 有名一元和文件测试操作符Perl的有些函数实际是一元操作符,这些函数包括:
-X (file tests) |
gethostbyname |
localtime |
return |
alarm |
getnetbyname |
lock |
rmdir |
caller |
getpgrp |
log |
scalar |
chdir |
getprotobyname |
lstat |
sin |
chroot |
glob |
my |
sleep |
cos |
gmtime |
oct |
sqrt |
defined |
goto |
ord |
srand |
delete |
hex |
guotemeta |
stat |
do |
int |
rand |
uc |
eval |
lc |
readlink |
ucfirst |
exists |
lcfirst |
ref |
umast |
exit |
length |
require |
undef |
文件测试操作符及其意义如下:
Operator |
Meaning |
-r |
File is readable by effective UID/GID |
-w |
File is writable by effective UID/GID |
-x |
File is executable by effective UID/GID |
-o |
File is owned by effective UID |
-R |
File is readable by real UID/GID |
-W |
File is writable by real UID/GID |
-X |
File is executable by real UID/GID |
-O |
File is owned by real UID |
-e |
File exists |
-z |
File has zero size |
-s |
File has nonzero size (returns size) |
-f |
File is a plain file |
-d |
File is a directory |
-l |
File is a symbolic link |
-p |
File is a named pipe(FIFO) |
-S |
File is a socket |
-b |
File is block special file |
-c |
File is a character special file |
-t |
Filehandle is opened to a tty |
-u |
File has setuid bit set |
-g |
File has setgid bit set |
-k |
File has sticky bit set |
-T |
File is a text file |
-B |
File is binary file (opposite of –T) |
-M |
Age of file (at startup) in days since modification |
-A |
Age of file (at startup) in days since last access |
-C |
Age of file (at startup) in days since inode change |
使用注意事项:
l 一元有名操作符的默认参数一般为$_,next if length < 80必须用next if length() < 80;
l 一元有名操作符的优先级比一些二元操作符高,所以注意sleep 4 | 3的意义;
l 文件测试时_表示延续上一测试(包括stat函数)的文件,比如print “Can do./n” if –r $a || -w _ || -x _;
3.3.10 关系操作符应注意Perl有两套比较操作符,针对数值与针对字符串的,两者不可混用!这是Perl相对于C特别之处!Perl的关系操作符如下:
Numeric |
String |
Meaning |
> |
gt |
Greater than |
>= |
ge |
Greater than or equal to |
< |
lt |
Less than |
<= |
le |
Less than or equal to |
== |
eq |
Equal to |
!= |
ne |
Not equal to |
<=> |
cmp |
Comparison, with signed result |
另外需注意:后三套操作符的优先级比前四套低;还有<=>和cmp是Perl特有的关系操作符,如果左操作数比右操作数小它返回-1,如果相等返回0,如果左操作数大于右操作数返回+1。<=>操作符被称为“宇宙飞船”操作符(spaceship operator)。
3.3.11 位操作符Perl的位操作符&(AND)、|(OR)、^(XOR)针对数值与字符串时有不同操作:如果任一操作数是数,两个操作数都被转化为整数;如果两个操作数都是字符串,对这个字符串的每一位做位操作。如:
print “12345” & “23456” #prints 02044
print 12345 & 23456; #prints 4128
3.3.12 C风格逻辑操作符Perl提供与C类似的&&(logical AND)和||(logical OR)操作符。但是应注意:Perl的&&与||的返回值的方式与C不同:它不是返回0或1,而是返回最后一个计算的值!
所以可以通过如下方式赋值:
$home = $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($<))[7] || die “You’are homeless!/n”;
$and = “abc” && “def” ; #$and值为def
$or = “abc” || “def”; #$or 值为abc
3.3.13 范围操作符范围操作符..实际是两个完全不同的操作符,针对不同环境意义不同:
l 在标量环境中,返回一个Boolean值,它模拟sed和awk的逗号操作符: if (2..10) {print;}
l 在标量环境,(3..n)返回1、2、…n-2E0,最后一个量附加了额外的“E0”。
l ..与…的差别是…操作符在比较左值为真后不对右值进行比较:所以if(3..3) {print:}将不打印任何行,而if(3…3){print;}将打印第3行之后的所有行。
l 在列表(List)环境中,返回从左值到右值的一个列表,注意左右值可以是数值也可以是字符串,如(‘aa’..’zz’)或(2..10)。
3.3.14 条件操作符条件操作符的格式是:COND ? THEN : ELSE
比如,以下语句是很常见的:
printf “I have %d camel%s./n” $n, $==1 ? ”” : “s”;
另外,注意条件操作符的优先级。下面语句可以判别某一年是否闰年:
$leapyear =
$year % 4 == 0
? $year % 100 ==0
? $year % 400 == 0
? 1
: 0
:1
:0;
$leapyear =
$year % 4 ? 0:
$year % 100 ? 1:
$year % 400? 0:1;
3.3.14 赋值操作符Perl提供C的所有的赋值操作符,同时增加了一些自己特有的。有很多赋值操作符,如
**= x= %= &&= ||= += -= ^=
TARGET OP= EXPR 等价于 TARGET = TARGET OP EXPR;
注意事项:
l TARGET只求值一次,所以$var[$a++] += $value中$a只增一次
l 与C不同的是,Perl的赋值表达式产生一个有效的左值(lvalue)。
比如: ($a += 2) *= 3;等价于$a += 2; $a *= 3;
这个语句也很常见: ($new = $old) =~ s/foo/bar/g;
3.3.15 逗号操作符注在标量环境中,它计算左边的所有值,扔掉,返回最右边的值。注意以下语句结果的不同:
$a = (1, 3); #$a=3
($a) = (1,3) #$a=1
@a=(1,3) #@a=qw/1 3/;
=>大多数时候只是逗号操作符的同义操作符。列表操作符的优先级比逗号操作符低。
3.3.16 逻辑and, or, not和xor操作符这四个逻辑操作符比相应C操作符优先级低。所以
unlink “alpha”, “beta”, “gamma” or gripe(), next LINE; 与
unlink(“alpha”, “beta”, “gamma”) || (gripe(), next LINE);等价。
但应注意or与||不总能互换,比如:
$xyz = $x || $y || $z; 与 $xyz = $x or $y or $z;意义是不同的!
xor操作在C与Perl中都没有对应, 因为左右两边都会计算,不会short-circuit。
与C操作符的比较 3.4.1 Perl操作符的特别之处
l 乘方(**)、比较(<=> )、模式匹配(=~ !~)、字符串连接(.)、区域操作( .. …)C没有;
l Perl对数值和字符串有两套不同的关系(比较)操作符,应注意区分。
3.4.1 C有Perl没有的操作符Perl没有C的如下操作符:取址操作符(&)、指针操作符(*,用来dereference)、类型转换操作符((TYPE))。
说明:Perl没有地址,所以不需要dereference一个地址,它的确有引用,它用$、@、%、&来反引。Perl也有*符号,也用表示一个typeglob。
Perl程序由一系列声明(Declaration)与语句(Statement)构成。声明主要在编译期间起作用。Perl不要求变量显式声明,它在初次使用时自动生成,如果未被赋值,在数值环境中会被作为0,在字符串环境中会被当成””,在作为逻辑变量时将作为false。但是Perl一般使用use strict;防止使用未定义变量。
语句按复杂程度可分为简单语句、复合语句,按逻辑关系可分为条件语句、循环语句等。
简单语句简单语句必须以分号结束,除非它是一个块的最后语句。一个简单语句可以加以下修饰符:
if EXPR
unless EXPR
while EXPR
until EXPR
foreach LIST
比如:
$trash->take(‘out’) if $you_love_me;
shutup() unless $you_want_me_to_leave;
kiss(‘me’) until $I_die;
$expression++ while –e “$file$expression”;
s/java/perl/ for @resumes;
print “field: $_/n” foreach split /:/, $dataline;
注意事项:
l Perl的if、unless语句正常使用时后面必须是BLOCK,哪怕只有一句话,也必须加{ },这对于C程序员会觉得很别扭,但是Perl也提供了替代方案,就是将if、unless作为后修饰符。
l Perl的if与unless,while与until完全起相反的功能:unless EXPR等价于if (! EXPR), until EXPR等价于while (!EXPR)。
l Perl的until语句与C的do{ }until语句的意义是不同的!Perl的until是与while相反的。until (EXPR)相当于while (! EXPR)。先测试后执行,而不是无条件执行。以后两个例子的输出是完全不同的:
$i = 0; do{ print “abc”} until ($i == 0); # 会输出abc
$i= 0; print “abc” until($==0); # 不会输出abc
即until在没有do时在最开始也会对条件进行判断,以决定是否执行前面的语句。
复合语句在一个范围内一系列语句称为一个块(BLOCK),复合语句是由表达式与BLOCKs组成,表达式由项与操作符组成。复合语句又可以分为复合条件语句与复合循环语句。
4.2.1 条件语句(if/unless语句)条件语句的语句如下:
if (EXPR) BLOCK
if (EXPR) BLOCK else BLOCK
if (EXPR) BLOCK elsif (EXPR) BLOCK
if (EXPR) BLOCK elsif (EXPR) BLOCK … else BLOCK
unless (EXPR) BLOCK
unless (EXPR) BLOCK else BLOCK
unless (EXPR) BLOCK elsif (EXPR) BLOCK
unless (EXPR) BLOCK elsif (EXPR) BLOCK … else BLOCK
说明:
l if与unless是互补的,unless ($x == 1) 等价于 if($x != 1)或者 if (! ($x == 1)) 。
l if/unless语句后面都是跟BLOCK,即必须有{ },哪怕只有一条语句!这是Perl很特别的地方。但是有不用括号的方法,即使用简单if/unless语句。
l 注意Perl使用elsif, 试比较C之else if ,好像是缺了一个字母似地!
l 变量声明的范围从声明开始之处到所有本条件语句之内,包括elsif和else在内。如:
if ((my $color =
$value = 0xff0000;
}
elsif ($color =~ /green/i) {
$value = 0x00ff00;
}
else{
warn “Unknown RGB component/n”;
$value = 0x000000;
}
语句)循环语句的语句如下:
LABEL while (EXPR) BLOCK
LABEL while (EXPR) BLOCK continue BLOCK
LABEL until (EXPR) BLOCK
LABEL until (EXPR) BLOCK continue BLOCK
LABEL for (EXPR: EXPR: EXPR) BLOCK
LABEL foreach (LIST) BLOCK
LABEL foreach VAR (LIST) BLOCK
LABEL foreach VAR (LIST) BLOCK continue BLOCK
例如:
LINE: while
(
next LINE if /^#/; # skip comments
next LINE if /^$/; # skip blank lines
…
} continue {
$count++;
}
LINE: while (defined($line =
chomp($line);
if ($line =~ s///$//) {
$line
.=
redo LINE unless eof(ARGV);
}
# now process $line
}
说明:
l while/until的进行的判断是互补的,until也是先判断再决定是否执行语句。
l LABEL是可选的,但是在Perl实践中还是经常使用,特别是循环内有next/last/redo语句的时候。
l 注意for和foreach关键字在实践上可以互换!但两者概念上是不同的。但是系统可以分别是for还是foreach操作。
l foreach是直接对数组元素操作的,变量是真正数组元素的别名,这一点一定要清楚。比如@arr=(1,2,3,4,5); foreach $i(@arr){ $i++} @arr就变成了(2,3,4,5,6)。
l 循环控制操作符如下:
last LABEL (相当于C的break)
next LABEL (相当于C的continue)
redo LABEL
LABEL是可选的,如果省略,表示最内层循环。
应明白: redo/last后不执行continue BLOCK。
l 在结构化编程的最初阶段,有些人坚持循环与子程序只能有一个入口和一个出口。One-entry是一个好的思想,但是one-exit通常是不太现实的,所以Perl建议按照需要退出循环。
l last/redo/next可以用于BLOCK,但是eval{}, sub{}, do{}却不属于循环BLOCK,同样if/unless中也不能直接使用。要使用的话,必须再加一层{}。例如:
if (/pattern/) {{
last if /alpha/;
last if /beta/;
last if /gamma/;
# do something her only if still in if()
}}
do {{
next if $x == $y;
# do something here
}} until $x++ > $z;
{
do {
last if $x = $y ** 2;
# do something here
} while $x++ <= $z;
}
注意:如果仍然用 do {{ last if $x = $y ** 2 }} where $x++ <= $z; 那么last起不到作用!后面的while继续运行。
Perl没有正式的switch和case语句。这是因为Perl并不需要,可以用以下方式达到同样目的:
SWITCH: {
if (/^abc/) { $abc = 1; last SWITCH; }
if (/^def/) { $def = 1; last SWITCH; }
if (/^xyz/) { $xyz = 1; last SWITCH; }
$nothing = 1;
}
SWITCH: {
/^abc/ && do { $abc = 1; last SWITCH; }
/^def/ && do { $def = 1; last SWITCH; }
/^xyz/ && do { $xyz = 1; last SWITCH; }
$nothing = 1;
}
子程序简介
子程序又称为函数。在Perl里子程序与函数是同一个概念。Perl允许用户自定义子程序,它们可以在主程序的任何位置,从其它文件中通过do/require/use关键字引入,或者使用eval在运行时生成,或者通过无名子程序的引用使用。
Perl的输入、输出模型非常简单:所有的输入的参数都是转化为一个标量的List,所有的函数都返回一个List列表。Perl中,数组变量@_是一个特殊的系统变量。如果函数调用时后面跟着一个用括号括起来的列表,则在函数调用期间该列表将被自动分配给一个以@_命名的特殊变量。@_是一个局部变量,它的值只在它出现的函数中有定义。return用来返回参数,如果return后面不带参数,在List环境中将返回空列表,在标量环境中将返回undef,在Boolean环境中将返回void。
声明子程序:
sub NAME; # A "forward" declaration. sub NAME(PROTO); # ditto, but with prototypes sub NAME : ATTRS; # with attributes sub NAME(PROTO) : ATTRS; # with attributes and prototypes 定义子程序: sub NAME BLOCK # A declaration and a definition. sub NAME(PROTO) BLOCK # ditto, but with prototypes sub NAME : ATTRS BLOCK # with attributes sub NAME(PROTO) : ATTRS BLOCK # with prototypes and attributes定义无名子程序:
$subref = sub BLOCK; # no proto $subref = sub (PROTO) BLOCK; # with proto $subref = sub : ATTRS BLOCK; # with attributes $subref = sub (PROTO) : ATTRS BLOCK; # with proto and attributes从模块中引入子程序:
use MODULE qw(NAME1 NAME2 NAME3);使用子程序:
NAME(LIST); # & is optional with parentheses. NAME LIST; # Parentheses optional if predeclared/imported. &NAME(LIST); # Circumvent prototypes. &NAME; # Makes current @_ visible to called subroutine. &$subref(LIST) $subref->(LIST) &$subref注意:有带&与不带&的使用方式,Perl5后建议不用&。但是若NAME不带参数不带任何参数时,及需要将通过函数引用使用函数时( &$subref() or &{$subref}())则不可省略(但是也可以用$subref->()代替)。
例如:
sub max { my $max = shift(@_); foreach $foo (@_) { $max = $foo if $max < $foo; } return $max; } $bestday = max($mon,$tue,$wed,$thu,$fri);
@common = inter( /%foo, /