Chinaunix首页 | 论坛 | 博客
  • 博客访问: 16681
  • 博文数量: 8
  • 博客积分: 843
  • 博客等级: 准尉
  • 技术积分: 91
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-02 13:20
文章分类
文章存档

2010年(8)

我的朋友

分类:

2010-09-06 16:24:40



LIST

  对于Perl来说,没有list类型,list只是一组值,可以表示为像“1, 2, 3, 4, 5”一样逗号分开的值,而由于逗号的优先级很低,所以经常用(...)包围起来,比如:
my @ar = 1, 2, 3, 4, 5;    # wrong
my @ar = (1, 2, 3, 4, 5);    # right

对于 .. 运算符来说,(...)不是必要的:
my @ar = 1 .. 5;    # right
my @ar = (1 .. 5)    # better

对某些语法结构来说,(...)是需要的,比如foreach循环:
foreach my $elem 1, 2, 3, 4, 5    # wrong
foreach my $elem (1, 2, 3, 4, 5)    # right
foreach接受(...)或者qw(...)格式的列表。

我们提倡,把(...)当成是声明list的语法。像[...]声明匿名数组,{...}声明匿名散列。在用到list的地方,一般都用(...)包围起来。


函数参数列表
  对于函数调用来说,参数其实是一个列表,像func(a, b, c),调用func函数,提供(a, b, c)的参数。但是Perl里可以省略参数列表的(...)。什么时候不省略?什么省略?考查下面的例子:
open(FILE, ">", "output.txt");
my $data = ;
chomp $data;
print FILE $data;
close FILE;

hex("ff");
grep { $_ % 2 } @ar;

首先,像很多类C的语言,函数调用都加上(...),我们也偏好在Perl代码里加上(...)。一般说来,加上(...)比较像函数调用,不加则比较像语句。对于有多个参数的函数调用,一般都加上(...),以提高可读性。对于只有1个参数或者没有参数的函数调用来说,假如是突出被调函数的“函数性”,而加上(...)。否则,就是把被调函数当成语句,则不加(...):
hex("ff");
chomp $data;

更具体说,借用BASIC函数和子程序的概念,假如用了被调函数的返回值,则是函数调用,加上(...);否则是子程序调用,不加。
对于像print defined这些函数来说,一般都是当做语句看待,也就不加(...)。

其次,有一些特别的参数,比如像print的第一个参数FILEHANDLE,由于FILEHANDLE可以缺省,所以需要和其它要打印的参数区分开。而对grep sort map来说,都接受的一个代码块,但在list里,{...}被解释为匿名散列。这类参数,假如它们后面还跟着其它参数的话,则省略它们后面的逗号,以示区别,假如后面没参数的话:
local $_ = 10;
my $var = 5;
print STDOUT;
print $var;
$var = \*STDOUT;
print $var;

假如print的第一个参数是一个祼词,而把它当成输出句柄。否则当成输出数据;对于像eval sub这类接受一个语句块做成参数的函数来说:
eval
{
    print "here\n"
};

sub
{
    5 + 5
};

下面是错误的用法:
eval
({
    print "here\n"
});

sub
({
    5 + 5
});

在(...)中,语句块被当成普通的匿名散列解释。
综上所述,对于这些参数,我们不应该把它包含在(...)中。把(...)省略掉,或者是把(...)加在剩下的一般参数列表上:
print STDOUT 1, 2, 3, 4, 5, "\n";
map { $_ * 2 } (1, 2, 3, 4, 5);


古怪而复杂的缺省行为
  Perl的复杂性很多都是缺省造成的。最普遍的,对很多内置函数来说,缺省需要的单个scalar参数,用$_代替,缺省需要的单个array参数,用@_代替。

chomp;    # chomp $_;
shift;    # shift(@_);

有一部分缺省的行为很复杂,比如:
eof

不是检测$_,而是检测最后用过的句柄。再比如:
split    # 会跳过$_开头的空白
不全等于:
split(/\s+/, $_);    # 不会跳过$_开头的空白

缺省会带来解释的二义性。如上面说到的(...),假如被缺省,而第一个参数又是一个list切片的话,会引起语法错:
func (1, 2, 3, 4, 5)[2, 3];
sub func
{
    warn "@_";
}

在Perl看来,变成了func(1, 2, 3, 4, 5) [2, 3],调用func,提供参数(1, 2, 3, 4, 5),再跟着[2, 3],所以引起语句错。再如:
print (1, 2, 3, 4, 5)[2, 3];    # 同理
要分別改为func + (1, 2, 3, 4, 5)[2, 3]和print + (1, 2, 3, 4, 5)[2, 3]。再比如:
my @ar = @{shift};
Perl偏好把@{shift}解释为@shift,要改为@{+shift}才是正确的。
这里,那个+号出现得相当诡异。把代码写全是个更好的方案:
func((1, 2, 3, 4, 5)[2, 3]);
print ((1, 2, 3, 4, 5)[2, 3]);
my @ar = @{shift(@_)};

调用函数,可以选择加不加&前缀,缺省在这里也会造成问题:
sub func
{
    warn "@_";
}

local @_ = (1, 2, 3, 4, 5);
func;
&func;    # 等价于 &func(@_);

两种调用方式都缺省了参数列表,但对于&方式调用,缺省参数列表,却是暗指用@_!!
所以,我们提倡,尽量不使用缺省。


全局变量的使用
$_的使用
  Perl提供了大量的预定义全局变量,比如常用的$_。全局变量提供了相当的方便,也带来了一些问题。Perl提供了local来局部化全局变量以补救。有一些语法结构,如map sort grep foreach也会自动局部会用到的全局变量。但不注意的话,还是会带来问题:
package FOO;
{
    sub func
    {
        # some code here
        $_ = 5;    # oops
        # some code here
    }
}

package main;
{
    while ()
    {
        FOO::func();
        print;
    }
}

__DATA__
line1
line2
line3

这里,默认读入数据存到$_中,又调用了某一个设计得不很好的包里面的函数,函数里修改了$_,导致结果不对。
我们提倡,干脆不用$_。要用的话,尽早把$_的值保存在安全的局部变量中或者尽早使用之。设计包的话,用到$_,先局部化,避免影响外部的代码。

减少代码里预定义变量的出现
  把预定义变量封装为一般的函数调用,向上层代码提供清淅的接口,比如:
# common.pm
sub autoflush
{
     select((select($_[0]), $| = 1)[0]);
}

上层的代码都调用autoflush,比直接用$|清淅,也比载入IO::Handle调用里面的autoflush方法快。


其它语法问题
prototype
  Perl里一般不用函数的prototype,因为有一些问题。一般写:
sub func
{
    my ($a, $b, $c) = @_;
}

而不写:
sub func($$$)
{
    my ($a, $b, $c) = @_;
}

做为替代,用一句my (...) = @_来描述参数原型,就算只有一个参数,也写
my ($arg) = @_;

花括号后面的分号
  匿名函数定义、eval等的参数里的花括号后面的分号不能省,因为整个花括号里的语句块是sub和eval的参数,相当于调用sub和eval函数,所以不能省。而具名函数的定义则是语法结构,跟匿名函数定义不是同一个概念。

next last redo
  next last redo可以作用于循环块,甚至一般的语句块:
{
    print "loop\n";
    redo;
}

但不作用于判断语句,因为:
while ($n < 10)
{
    if ($n == $m)
    {
        last;
    }
}

像这样子,经常用判断语句,来跳出循环;在做为参数的语句块里,next last redo也是不作用的,像:
do
{
    last;
} while (1);

do是一个函数,接受一个语句块为参数,后面跟可选的while等修饰符。在do while中,last并不知道语句块的存在,要改为:
{
    do
    {
        last;
    } while (1);
}

留白和对齐
  单目操作符前后不留空,双目操作符前后留空,像!$var, $n++, --$m, $c = $a + $b。几行起同一功能的语句后面留空一行,标志一个逻辑块的结束。语句块后面留空一行。
花括号的对齐,采用:
sub func
{
    ;
}

而不是:
sub func {
    ;
}

因为第2种方式的写法像脚本。假如语句块稍一长,第1种方式也容易找到语句块的开始。


代码效率的一般建议
  没理由,不要发明内置函数已有的功能,尽量用内置的,因为那些内置函数是用C写的。比如,不要写自己的快速排序,而是用sort的排序子函数。
在代码效率和代码清淅有冲突的情况下,假如是写包里的代码,选择效率高的方式。假如是写上层的代码,选择清淅的方式。比如,在包里用$#array,在上层代码里,用scalar(@array)。

appleii
newestbie@gmail.com
2010.09.06

阅读(766) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-09-08 10:12:16

Download More than 1000 free IT eBooks: http://free-ebooks.appspot.com