Perl风格:各有所爱

Perl风格:各有所爱,第1张

概述*风格问题很容易变成信仰问题。 *我要告诉你们的绝大部分是个人意见。其中既有泛泛之论,也有指路明灯。 *警告:我不一定总是按自己说的做!:) *我并不期望你们永远和我保持一致。选择一种风格,坚持下去。连贯性才是最重要的! *K&P, K&R, S&W, Rob Pike, 和Larry Wall的基础性工作对本文有间接贡献。Jon Orwant,  Mark-Jason Dominus, 和Nat *风格问题很容易变成信仰问题。 *我要告诉你们的绝大部分是个人意见。其中既有泛泛之论,也有指路明灯。 *警告:我不一定总是按自己说的做!:) *我并不期望你们永远和我保持一致。选择一种风格,坚持下去。连贯性才是最重要的! *K&P,K&R,S&W,Rob Pike,和Larry Wall的基础性工作对本文有间接贡献。Jon Orwant,  Mark-Jason Dominus,和Nat Torkington则直接参与了本文草稿的审阅。 *Rob Pike说:“无论如何不要因为我说怎样编程你就怎样编程。如何编程取决于你对程序目的和 如何表现这一目的的认识。照这样坚持不懈、一丝不苟地做下去。” Perl风格:写Perl程序,不是C/BASIC/Java/Pascal等等 *<<Perl编程>>中说:“仅仅因为你能用某种方式做事并不意味着你就应该用这种方式做事。” *当你发现自己写的代码看上去象C,或BASIC,或JAVA,或PASCAL,你很可能正在欺骗自己。你 需要学习写符合习惯的Perl -- 这不是说写晦涩的Perl程序。这说的是写有Perl特色的Perl程序: 原汁Perl。 *有人说某些Perl习惯应该避免,否则负责维护代码的人如果不懂Perl就理解不了程序。这实属大 谬,可笑已极。如果不懂Perl就别维护Perl程序。你写英语的时候不会考虑法国人、德国人懂不懂, 写希腊语的时候也不会用拉丁文。 Perl风格:大方 *Dennis RitchIE说:“要做到既正确(紧凑、无误)又可用(统一、有吸引力),难。” *写程序要努力做到有用,精练,灵活,易懂 -- 不一定是按这个顺序。 *某些情况下把程序写得短一些可以改善可维护性,另一些情况下就不行。 Perl风格:谨慎编程 *use strict *#!/usr/bin/perl -w *检查所有系统调用的返回值,打印$! *用$?检查外部程序的失败。 *在eval或s///ee之后检查$@。 *参数声明。 *#!/usr/bin/perl -T *在一串elsif后一定要加一个else *在列表的最后放个逗号,这样别人在列表后面再加点东西的时候就不会出错。 Perl风格:注释艺术 *解释代码的作用,而不只是把代码翻成英语(汉语)。 *不要弄成花哨的招牌式的东西。 *用/x在正则表达式里加上注解。 *给整段代码加注,而不是给单句加注。 *Rob Pike说:“给数据加注比给算法加注通常要有用得多。” *Rob Pike说:“基本避免注解。如果你的程序需要注解才能让人理解,最好重写,让它更好懂一些。” Perl风格:命名原则(形式) *Rob Pike说:“我不会在名字里放大写字母。对我看惯了散文的双眼来说它们看上去很不舒服,就象是 些拼写错误一样晃来晃去。” *`IEschewEmbeddedCAPItalLettersInnames ToMyProSEOrIEntedEyes  TheyAretooAwkwardToReadComfortably TheyJanglelikeBadTypography.' (译注:这是重复上 句的原文,用原文所反对的风格写出的,看上去很难受吧!) *虽然把两三个短词放在一起做名字比如$getit大概没事,最好还是用下划线把单词分隔开。一般说来 $var_names_like_this比$VarnameslikeThis要好读,特别是对于不说英语的人。这条简单的规则和 VAR_nameS_liKE_THIS这样的变量名也能合作愉快。 *用变量名的大小写表示变量的范围和性质会很有帮助。例:    $ALL_CAPS_HERE   只用于常量(小心和Perl的内在变量名冲突!)    $Some_Caps_Here  全局变量、静态变量    $no_caps_here    函数范围的 my() 或 local() 变量 *函数或方法名称最好全用小写,比如:$obj->as_string(); Perl风格:命名原则(内容) *Rob Pike说:“过程的名称应该反映它做了什么,函数的名称应该反映它返回什么。” *对象命名应使其阅读顺利。比如,谓语性质的函数通常用"is","does","can","has"命名。所以, $is_ready是比$ready更好的函数名。 *如上所述,&cannonize是过程名,&canonical_version表示一个返回值的函数,而&is_canonical 做一个布尔量检查。 *象&abc2xyz或&abc_to_xyz这样形式的名称为转换函数或哈希表对映函数所普遍采用。 *哈希表通常表示键的性质,代表“某物所拥有的”这样一个概念。所以为哈希表中的值来命名哈希,而不是 为键来命名。    好例:         %color = ('apple' => 'red','banana' => 'yellow');         print $color{'apple'};          # Prints `red'    坏例:         %fruit = ('apple' => 'red','banana' => 'yellow');         print $fruit{'apple'};          # Prints `red' Perl风格:变量名长度 *Mark-Jason Dominus说:“合适的变量名长度与其作用范围的大小成反比。” *变量名长不是优点,清楚才是。不要这样写:     for ($index = 0; $index < @$array_pointer; $index++) {          $array_pointer->[$index] += 2;     } 应该这样写:     for ($i = 0; $i < @$ap; $i++) {          $ap->[$i] += 2;     } (虽然你可以说某个变量名会比$ap好,但也不见得....) *全局变量名应该比局部变量名长一些,因为它们的语境比较难发现一些。比如%state_table是一个程序 的全局变量,而$func只是一个局部指针。     foreach $func (values %state_table) { ... } Perl风格:并列对齐 *一致性和并列对齐能使代码可读性大大增加。比较这段代码:     my $filename =    $args{PATHname};     my @names    = @{ $args{FIELDnameS} };     my $tab      =    $args{SEParaTOR}; 和这段代码:     my $filename = $args{PATHname};     my @names = @{$args{FIELDnameS}};     my $tab = $args{SEParaTOR}; *把注释和所有的|| dIE语句对齐放在一列上,象这样:     socket(SERVER,PF_UNIX,SOCK_STREAM,0) || dIE "socket $sockname: $!";     bind  (SERVER,$uaddr)                  || dIE "bind $sockname: $!";     Listen(SERVER,SOMAXCONN)                || dIE "Listen $sockname: $!"; Perl风格:在控制和赋值里多用&&和|| *Perl里的&&和|| *** 作符会象C里的一样短路,但返回值不一样:Perl返回的是第一个满足条件的值。 *以下情况经常用||来完成:     ++$count{ $shell || "/bin/sh" };     $a = $b || 'DEFAulT';     $x ||= 'DEFAulT'; *时候也可以用&&来完成,通常返回的假值是空值而不是0。(在Perl里测试为假返回的是空值而 不是0!)     $nulled_href = $href . ($add_nulls && "\0"); Perl风格:学习优先性 *可以象使用标点符号一样使用and和or的说法是胡说八道。它们的结合优先性不同。你^必须^学 会结合优先性。多用括号不会有坏处。    print FH $data      || dIE "Can't write to FH: $!";  # 错    print FH $data      or dIE "Can't write to FH: $!";  # 对    $a = $b or $c;      # 错了,是个虫虫    ($a = $b) or $c;    # 等于这样    $a = $b || $c;      # 应该这么写    @info = stat($file) || dIE;     # 嘿,stat()会返回一个单值    @info = stat($file) or dIE;     # 这样才对 *这个怎么加括号?    $a % 2 ? $a += 10 : $a += 2 是这个意思:    (($a % 2) ? ($a += 10) : $a) += 2 而不是:    ($a % 2) ? ($a += 10) : ($a += 2) Perl风格:别把?:用过了头 *在控制结构中用?:会带来麻烦。最好还是用if/else。千万别在控制结构中嵌套使用?:     # 坏:     ($pID = fork) ? waitpID($pID,0) : exec @ARGS;     # 好:     if ($pID = fork) {         waitpID($pID,0);     } else {         dIE "can't fork: $!"    unless defined $pID;         exec @ARGS;         dIE "can't exec @ARGS: $!";     } *最好当表达式用:     $State = (param() != 0) ? "RevIEw" : "Initial";     printf "%-25s %s\n",$Date{$url}             ? (scalar localtime $Date{$url})             : "<NONE SPECIFIED>",Perl风格:不要定义TRUE和FALSE *Perl理解布尔量,不要试图自行定义。以下的代码十分糟糕:      $TRUE  = (1 == 1);      $FALSE = (0 == 1);      if ( ($var =~ /pattern/ == $TRUE  ) { .... }      if ( ($var =~ /pattern/ == $FALSE ) { .... }      if ( ($var =~ /pattern/ eq $TRUE  ) { .... }      if ( ($var =~ /pattern/ eq $FALSE ) { .... }      sub getone { return "This string is true" }      if ( getone() == $TRUE  ) { .... }      if ( getone() == $FALSE ) { .... }      if ( getone() eq $TRUE  ) { .... }      if ( getone() eq $FALSE ) { .... } *想象一下如下的引申是多么愚蠢,还是在第一个语句后就停下吧。      if (    getone() )                  { .... }                         if (    getone() == $TRUE  )      { .... }      if (   (getone() == $TRUE) == $TRUE  )      { .... }      if ( ( (getone() == $TRUE) == $TRUE) == $TRUE  ) { .... } Perl风格:多用正则表达式 *正则表达式是你的朋友。不仅如此,它还是一种全新的思考方式。 *就象象棋选手会在棋盘上的子力分布上看出模式,Perl对分析数据中的模式很拿手。虽然大多数现代 编程语言都提供一些模式匹配的基本工具,通常是用外带的库,但Perl的模式匹配可是直接整合在语言核 心里的。象/..../,$1什么的。 *Perl的模式匹配提供许多别的语言没有的强大功能,这些功能会鼓励你用一套全新的眼光看待数据。 Perl风格:边走边换 *拷贝和替换可以一次完成。例:     chomp($answer = <TTY>);     ($a += $b) *= 2;     # 把路径名去掉     ($progname = $0)        =~ s!^.*/!!;     # 所有单词第一字母大写     ($capword  = $word)     =~ s/(\w+)/\u\L$1/g;     # /usr/man/man3/foo.1 换成 /usr/man/cat3/foo.1     ($catpage  = $manpage)  =~ s/man(?=\d)/cat/;     @bindirs = qw( /usr/bin /bin /usr/local/bin );     for (@libdirs = @bindirs) { s/bin/lib/ }     print "@libdirs\n";   | /usr/lib /lib /usr/local/lib Perl风格:用负数作数列下标 *要得到数列的最后一个元素,用$array[-1]而不要用$array[$#array]。前者对列表和数列都有效, 而后者不是。 *请记住substr,index,rindex和splice都接受负数下标,从列表尾部开始计数。   split(@array,-2);   # 两次pop *请记住substr可以被赋值。例:     substr($s,-10) =~ s/ /./g; Perl风格:多用哈希 *当你用哈希方式思考的时候才开始理解Perl。哈希常常可以代替冗长的循环和复杂的算法。 *当你想表达一个集,或关系,或表,或结构,或记录的时候,请用哈希。 *“在。。。中”,“唯一”,“第一”,“重复”这样的字眼应该引起你条件反射式的大喊:“哈希!”如果你把 这些字眼和“列表”放在一个句子里,恐怕有些东西不太对头。 Perl风格:用哈希处理集 *下面这段代码找出两个列表@a和@b的并集和交集:     foreach $e (@a) { $union{$e} = 1 }     foreach $e (@b) {         if ( $union{$e} ) { $isect{$e} = 1 }         $union{$e} = 1;     }     @union = keys %union;     @isect = keys %isect; 下面的代码做的是同样的事情,而利用了Perl的特色:     foreach $e (@a,@b) { $union{$e}++ && $isect{$e}++ }     @union = keys %union;     @isect = keys %isect; Perl风格:用哈希记录做过什么 *哈希是追踪你有没有做过什么事的好办法。 *多用 ... unless $seen{$item}++ 这样的办法,例:     %seen = ();     foreach $item (genList()) {         func($item) unless $seen{$item}++;     } Perl风格:用哈希储存记录,不要用并行列表 *学习使用哈希结构储存记录,再把这些记录保存在列表或哈希里,不要用并行列表,象这样:     $age{"Jason"} = 23;     $dad{"Jason"} = "Herbert"; 应该这样:     $people{"Jason"}{AGE} = 23;     $people{"Jason"}{DAD} = "Herbert"; 或者这样(注意这里for的用法):     for $his ($people{"Jason"}) {         $his->{AGE} = 23;         $his->{DAD} = "Herbert";     } 不过在象下面这样做之前,最好^三思^:     @{ $people{"Jason"} }{"AGE","DAD"} = (23,"Herbert"); Perl风格:在代码不长时使用$_ *和新手的想法恰恰相反,使用$_可以改善代码的可读性。比较下面这段代码:     while ($line = <>) {         next if $line =~ /^#/;         $line =~ s/left/right/g;         $line =~ tr/A-Z/a-z/;         print "$ARGV:";         print $line;     } 和这段:     while ( <> ) {         next if /^#/;         s/left/right/g;         tr/A-Z/a-z/;         print "$ARGV:";         print;     } Perl风格:使用foreach()循环 *foreach循环的隐含重命名和局部化可是强力工具。例:     foreach $e (@a,@b) { $e *= 3.14159 }     for (@lines) {         chomp;         s/fred/barney/g;         tr[a-z][A-Z];     } *记住你可以把拷贝和修改一次完成:     foreach $n (@square = @single) { $n **= 2 } *你还可以用哈希片段来改变哈希值。例:     # 把单量、列表里的氖有值、哈希里的所有值中的     # 空白字符都去掉。     foreach ($scalar,@array,@hash{keys %hash}) {         s/^\s+//;         s/\s+$//;     } Perl风格:避免字节处理 *C程序员常常在处理字符串时一次处理一个字节。别这么做!Perl在处理大串字符时很轻松! *不要用getc,一次抓一整行,对一整行进行处理。 *即使是传统上在C语言里一次处理一个字符的 *** 作,比如语义分析,也应采用不同的方法。例:     @chars = split //,$input;     while (@chars) {       $c = shift @chars;       # State machine;     } 这样的办法太底层了。试试这样:     sub parse_expr { local $_ = shift; my @tokens = (); my $paren = 0; my $want_term = 1; while (length) {    s/^\s*//;    if (s/^\(//) { return unless $want_term; push @tokens,'('; $paren++; $want_term = 1; next;    }     if (s/^\)//) { return if $want_term; push @tokens,')'; if ($paren < 1) {    return; }  --$paren; $want_term = 0; next;    }     if (s/^and\b//i || s/^&&?//) { return if $want_term; push @tokens,'&'; $want_term = 1; next;    }     if (s/^or\b//i || s/^\|\|?//) { return if $want_term; push @tokens,'|'; $want_term = 1; next;    }     if (s/^not\b//i || s/^~// || s/^!//) { return unless $want_term; push @tokens,'~'; $want_term = 1; next;    }     if (s/^(\w+)//) { push @tokens,'&' unless $want_term; push @tokens,$1 . '()'; $want_term = 0; next;    }     return; } return "@tokens";     } Perl风格:避免使用符号引用 *新手常常想用一个变量包含另一个变量的名字:     $fred    = 23;     $varname = "fred";     ++$varname;         # $fred Now 24 *有时候这也奏效,不过这归根结底是个馊点子。^符号引用只对全局变量有效^,而全局变量是要大力避免 的,太容易引起重名冲突了。 *当你用了use strict的时候,它就失效了。 *它们不是真正的引用,不计入引用计数,也不被Perl的垃圾回收站回收。 *应该使用哈希或真正的引用。 Perl风格:想用$$name的时候,用哈希 *用变量包含另一个变量的名字总是意味着此人对哈希掌握不够。虽然可以这样写:     $name = "fred";     $$name{WIFE} = "wilma";     # set %fred     $name = "barney";           # set %barney     $$name{WIFE} = "betty"; 最好还是这样写:     $folks{"fred"}  {WIFE} = "wilma";     $folks{"barney"}{WIFE} = "betty"; Perl风格:不要对eof作测试 *不要这样写(死锁):     while (!eof(STDIN)) {         statements;     } *应当这样写:     while (<STDIN>) {         statements;     } *在eof不成立的时候给用户提示是很烦人的。试试这个:    $on_a_tty = -t STDIN && -t STDOUT;    sub prompt { print "yes? " if $on_a_tty }    for ( prompt(); <STDIN>; prompt() ) {         statements;    } Perl风格:不要滥用反斜杠 *Perl允许你选用自定的分隔符来分隔模式和引文,这样可避免“牙签成堆综合症”(指许多反斜杠连续出现)。 请多用自定分隔符。例:     m#^/usr/spool/m(ail|queue)#     qq(Moms saID,"That's all,$kID.")     tr[a-z]       [A-Z];     s { /          }{::}gx;     s { \.p(m|od)$ }{}x; Perl风格:减少复杂性 *但当可能的时候请把next和redo放在循环的最前面。 *使用unless和until。 *但不要使用 unless ... else ...  *从Pascal的暴政下逃脱。不要在经历无谓的弯弯绕之后最后才退出循环或函数。不要这样写:     while (C1) {         if (C2) {             statement;             if (C3) {                 statements;             }         } else {             statements;         }     } Perl风格:减少复杂性(解决方案) *应该这样写:     while (C1) {         unless (C2) {             statement;             next;         }         statements;         next unless C3;         statements;     } *或者这样写:     while (C1) {         statement,next unless C2;         statements;         next unless C3;         statements;     } Perl风格:减少重复 *把重复代码放到段落之外。例:修改前:     if (...) {         X;  Y;     } else {         X;  Z;     } 修改后:     X;     if (...) {         Y;     } else {         Z;     } Perl风格:化整为零 *把子函数分成便于管理的单位。 *不要试图用一个正则表达式匹配所有的东西。 *在ARGV上下点工夫。例:     # 程序期待环境变量     @ARGV = keys %ENV       unless @ARGV;     # 程序期待源程序     @ARGV = glob("*.[chyC]") unless @ARGV;     # 对gzip文件也能行     # from Perl Cook Book 16.6     @ARGV = map { /^\.(gz|Z)$/ ? "gzip -dc $_ |" : $_  } @ARGV; Perl风格:把程序分为不同的进程 *学习使用特殊形式的open:     # from Perl Cookbook 16.5     head(100);     sub head {         my $lines = shift || 20;         return if $pID = open(STDOUT,"|-");         dIE "cannot fork: $!" unless defined $pID;         while (<STDIN>) {             print;             last if --$lines < 0;         }         exit;     } (译者按:读者最好阅读一下Perl Cookbook的相关章节,本节内容较深。) Perl风格:面向数据的编程 *数据结构比代码更重要。 *Rob Pike说:“数据至高无上。只要你选择了正确的数据结构并进行了合理的组织,算法总是不言自明的。 数据结构,而不是算法,是编程的核心。(参阅brooks,第102页)” *用数据概括普遍,用代码处理异常。(Kernighan) *如果在两个地方看到类似的功能,把它们统一起来。这叫做“子函数”。 *考虑做一个函数指针的哈希结构来代表状态表或switch语句。 Perl风格:配置文件 *如果需要配置文件,用do语句来加载。 *你于是得以使用Perl的全部威力:     # 摘自 Perl Cookbook 8.16     $APpdfLT = "/usr/local/share/myprog";     do "$APpdfLT/sysconfig.pl";     do "$ENV{HOME}/.myprogrc";     # 在配置文件中这样写     $NETMASK = '255.255.255.0';     $MTU     = 0x128;     $DEVICE  = 'cua1';     $RATE    = 115_200; *请在此处(http://www.perl.com/CPAN/authors/ID/TOMC/scripts/)参阅本文作者的clip和cliprc 文件。 Perl风格:函数作为数据 *将函数指针用在数据结构或用作函数参数。例:     # from MxScreen in TSA (see also PCB 19.12)     %state_table = (         Initial  => \&show_top,        Execute  => \&run_query,        Format   => \&get_format,        Login    => \&resister_login,        RevIEw   => \&revIEw_selections,        Sorting  => \&get_sorting,        Wizard   => \&wizards_only,    );     foreach my $state (sort keys %state_table) {         my $function = $State_table{$state};         my $how      = ($action == $function)                         ? SCREEN_disPLAY                         : SCREEN_HIDDEN;         $function->($how);     } Perl风格:闭包(closure) *用闭包克隆相似的函数。例:     # from MxScreen in TSA     no strict 'refs';     for my $color (qw[red yellow orange green blue purple violet]) {         *$color = sub { qq<<Font color="\U$color\E">@_</Font>> };     }     undef &yellow;      # lint happiness     *yellow = \&purple; # function aliasing *或类似地:     # from psgrep (in TSA,or PCB 1.18)     my %fIElds;     my @fIEldnames = qw(FLAGS UID PID PPID PRI NICE SIZE                         RSS WCHAN STAT TTY TIME COMMAND);     for my $name (@fIEldnames) {         no strict 'refs';         *$name = *{lc $name} = sub () { $fIElds{$name} };     } Perl风格:学习用for作开关语句 *虽然Perl没有switch语句,这实际上是个机会而不是困难。 *做一个开关控制很容易。for这个词有时念作“switch”    SWITCH: for ($where) {                /In Card names/     && do { push @flags,'-e'; last; };                /Anywhere/          && do { push @flags,'-h'; last; };                /In Rulings/        && do {                    last; };                dIE "unkNown value for form variable where: `$where'";            } *就像一系列的elsif,开关语句一定要有一个缺省设置。即使这种缺省情况“永远也不可能发生”。 Perl风格:创造性地用do{}语句来作开关语句 *另一种有趣的办法是用do{}语句返回的值来做成开关语句。例:    $amode = do {        if     ($flag & O_RDONLY) { "r" }       # XXX: isn't this 0?        elsif  ($flag & O_WRONLY) { ($flag & O_APPEND) ? "a" : "w" }        elsif  ($flag & O_RDWR)   {            if ($flag & O_CREAT)  { "w+" }            else                  { ($flag & O_APPEND) ? "a+" : "r+" }        }    }; Perl风格:用&&和||来作开关语句 *要小心,&&的右边总为真。    $dir = 'http://www.wins.uva.nl/~mes/jargon';    for ($ENV{http_USER_AGENT}) {        $page  =    /Mac/            && 'm/Macintrash.HTML'                 || /Win(dows )?NT/  && 'e/evilandrude.HTML'                 || /Win|MSIE|WebTV/ && 'm/Microslothwindows.HTML'                 || /linux/          && 'l/linux.HTML'                 || /HP-UX/          && 'h/HP-SUX.HTML'                 || /SunOS/          && 's/ScumOS.HTML'                 ||                     'a/AppendixB.HTML';    } Perl风格:更创造性地使用for和do来构造开关语句 *有时审美也很重要。:)     for ($^O) {         *struct_flock =                do                           {                                 /bsd/  &&  \&bsd_flock                                        ||                             /linux/    &&    \&linux_flock                                        ||                           /sunos/      &&      \&sunos_flock                                        ||                   dIE "unkNown operating system $^O,bailing out";         };     } Perl风格:管好模块 *使用Pod为你的模块写文档,用pod2man和pod2HTML作检查。 *使用Carp模块里的carp,croak,confess,不要用warn和dIE。 *写得好的模块很少需要用到::。用户应该可以用import和类函数得到模块内容。 *传统的模块也不错。不要因为用对象编程看着很酷就急急忙忙跳上去。明显需要的时候再用不迟。 *使用对象应该通过调用对象函数。 *对象函数本身也应该通过对象指针使用类数据。 Perl风格:补丁 *当你和别人写的代码打交道的时候,遵循他们的范例。 *不要因为会产生巨大的diff文件就重新样式化代码。 *有时候为了对付某些自定义tab键的空格数的坏蛋,把tab转换成空格似乎是必要的。但^别这么做^! 总结

以上是内存溢出为你收集整理的Perl风格:各有所爱全部内容,希望文章能够帮你解决Perl风格:各有所爱所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1293523.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存