`
jedy
  • 浏览: 147731 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

Perl:化繁为简

阅读更多

提高 Perl 代码的可靠性和可维护性的简单技巧

Teodor Zlatanov
程序员,Gold Software Systems

内容:
“没有改进余地”

编写循环
编写清除代码
使用 map 和 grep
使用模块
使用对象
使用 ptkdb
PSI::ESP 模块
结论
参考资料
关于作者

用 Perl 语言完成作业很容易。设计这种语言的目的是使简单的任务更容易,使困难的任务有可能完成。可是,该语言内置的简易性可能是一个陷阱。程序员生来不乐意编写 文档或设计程序的体系结构。编写纯代码带来的刺激在于与机器直接沟通,明确告诉它要做什么。Teodor Zlatanov 介绍了通过增加代码的清晰性提高 Perl 程序的可靠性和可维护性的技术。他的提示适用于初级或中级 Perl 程序员,着重强调建立好的标准而不是更改特殊的编码风格。

您不必改变想法(或编码!),就可 以提高 Perl 代码的清晰性。虽然用 Perl 编写复杂的任务是困难的,但却可以做到。并且可以很巧妙地做到。程序写完后,您不必是唯一可以理解和维护程序的人。使用这九个提示,您可以一直使用 Perl,保持您的风格,并且仍有一个可理解且稳定的程序。

“没有改进余地”
您可能在想:“我有几年的 C/C++/Ada/Assembler/Pascal/LISP/Java 经验,我的代码是完美的。别跟我说什么改进。”我的回答是:编程中没有完美可言,只有对完美的追求。优秀的程序员每天都在学习新知识,并不断改进技术。

作为一门语言,Perl 的可塑性很强。例如,可以如下打印环境变量(从版本 5.005)开始:

用一行代码打印 %ENV 内容 print "$_ => $ENV{$_}\n" foreach(sort keys %ENV);

或者可以:

用几行代码打印 %ENV 内容 foreach (sort keys %ENV)
{
print "$_ => $ENV{$_}\n";
}

甚至可以使用 Data::Dumper 模块:

用 Data::Dumper 打印 %ENV 内容 use Data::Dumper;
print Dumper(\%ENV);

来自 Perl 发行版的文档
Perl 文档(例如 "xyz" 页面)包含在 Perl 发行版中。可以在命令行输入 "perldoc xyz" 访问这些页面。也可以访问 HTML 格式的文档。

这些方法在调试环境中做同样的事。但是,哪一个更易于理解、编写和维护?当然是第三个。如果从没用过 Data::Dumper,则应该阅读文档 ("perldoc Data::Dumper") 并在程序中尝试。

速度不是改进程序代码的唯一标准。还应在任何软件项目中考虑测试、编写和维护的简易性。象 Perl 这样灵活的语言,使除预编码阶段(需求收集与体系结构设计)之外的任何软件项目阶段的优化编码易于进行。

编写注释:回忆以前编写的脚本时
并没有太多的文档这回事。“清楚”常意味着重复。想象一下将代码展现给全世界。世界上有很多人。您认为多余的一个注释可能会帮助别人更好地理解。五年后的今天,当您添加新特性时,也会感到方便。

编写程序时要作好计划。不必事先确定每个细节。但是应该将程序分成不同的组件部分,并用注释填充组件之间的间隙。

下面举一个小例子:该程序反转除在 /etc/hosts 文件中找到的名称之外的所有输入。

输入反转程序,第一版 #!/usr/bin/perl -w
# author, date, revision, etc.

# brief summary of program

# modules used summary
# pragmas used summary

# function A summary

# function B summary

# main loop summary

# POD documentation

下面详细说明每一段代码:

输入反转程序,第二版 #!/usr/bin/perl -w
# author, date, revision, etc.

# This program will process user input with two functions.
# Command-line arguments will be treated as input file sources.
# No flags are allowed.

# modules used: none
# pragmas used: strict
use strict;

# function A: return a parameter with the letters reversed

# function B: return true if a word is in the /etc/hosts file

# main loop: go through input lines, passing each word to B
# and, if B returns false, to A

__END__
POD documentation

最后,得到带有代码和注释的最终版本。查看脚本:reverser.pl。

编写有意义的循环
循环可分为单行循环和多行循环。避免将所有循环代码写入一行,即使看起来可能更自然。比较:

除首字母大写外、用小写字母打印并排序所有环境变量的单行循环 print ucfirst lc $_, "\n" foreach (sort keys %ENV);

和:

除首字母大写外、用小写字母打印并排序所有环境变量的多行循环 foreach (sort keys %ENV)
{
print ucfirst lc $_, "\n";
}

第二个循环与第一个循环做同样的事。在必须维护的生产代码中,您更愿意看到哪一个?对。多行循环更易于阅读。

试着忘记基于 C 的语言的坏习惯。一般说来,这是个好建议。但是在某些特殊循环中,情况可能会有不同。应该尽量少用 'for' 语句。

将 if(not 条件)变换成 unless(条件)。

If 变成 unless print "Yes" if not $false;
print "Yes" if !$false;
# 变成...
print "Yes" unless $false;

类似地,while(not 条件)等价于 until(条件)。

清单 9:while 变成 until print "No signal" while !$signal;
print "No signal" while not $signal;
# 变成...
print "No signal" until $signal;

'for' 循环是个坏注意。它将三件事浓缩成一行:初始条件、增量和结束条件。这在 Perl 出现之前是必需的,现在,'foreach' 循环使 'for' 循环不再需要。

例如,变量 $i 从 1 增加到 10:

用 foreach 迭代 foreach $i (1 .. 10)
{
# do something
}

与等价的 'for' 循环比较:

用 for 迭代 for ($i=1; $i <= 10; $i++)
{
# do something
}

在某些场合下,'for' 循环可能有用。但是强烈建议记下它的出现情况,并找出不使用 'foreach' 的充分理由。对于初学者和中级 Perl 程序员来说,使用 'for' 结构弊大于利。

清晰之路
如果怀念 C 时代的 lint 和 -Wall,那么仍然有希望。

使 用 -w 标志来运行脚本。对 Perl 解释器使用 'use strict' 指令,以使程序易读、结构化和更好地运行。您不会奇迹般地变成更好的程序员,但这却可以极大地改进代码。例如,不会在第一次使用时缺省创建变量和散列密 钥。必须声明变量,这样将避免很多常见故障。

有关如何使用 strict 和 -w 的例子,请参阅 reverser.pl。

不要将常数定义成变量或函数。'use constant' 伪指令可以很好地完成此工作。如果您的 Perl 不支持,则请升级。

Use constant 示例 use constant TIMESLICE => 15;

对函数使用原型。不要在进行中补充函数用法。在编码之前作好计划,这样,您的工作将从计划中受益非浅。

注意小数位、数字和字符串之间的区别。请阅读 FAQ,Programming (或者 Learning)Perl 一书。请大体完成这本书上的课后作业。Perl 使数字和字符串之间的转换易于进行,但是这可能会产生一些需要几小时或几天才能发现的小故障。

有关清晰的主题永远也说不完。应当不断努力编写清晰的代码,并不断学习。这些只是 Perl 语法良好用法的最基本部分。还要看一下 perlsyn 和 perlstyle 页面。它们包含有关此主题的中肯意见。

今晚 9 点的函数性程序员匿名会议
对 于那些希望 Perl 成为具有更强函数性语言的程序员,map 和 grep 可以满足此需求。Map 和 grep 对列表的每个元素计算块或表达式。它们在块语法中十分有用。我们过一会将看一些那个语法的更高级例子。map 和 grep 将 $_ 设置成正在块中检查的当前元素。

经常引用的 Schwartzian 变换实际只是它的其中一个字段的临时数组。这里有一个例子:

Schwartzian 变换 @sorted_list =
map { $_->[0] }
sort { $a->[1] <=> $b->[1] } # note that this is a numeric sort
map { [$_, index_function($_)] }
@unsorted_list;

未排序的列表可以包含任何想要的数据。编写一个 index_function 生成一个键,该键用于对未排序列表的元素进行排序。变换将按那些键的数字顺序对元素排序。

如果不理解这个代码清单,则应该仔细阅读有关 map 和 sort 函数的文档。学习理解匿名数组引用和解除数组元素引用的 Perl 符号。Effective Perl Programming 站点(请参阅参考资料)详细解释了 Schwartzian 变换。

map 和 grep 允许使用许多巧妙的技术。例如:

将数组映射成大写 @uc = map { uc } @values;

这将通过把 uc 功能映射成数组中的每个元素,将数组转换成大写。我们以前看到过吗?是的!打印环境变量的循环可以从此方法中受益,以提高易读性:

使用 map,并且除首字母大写外、以小写字母打印并排序所有环境变量的单行循环 print $_, "\n" foreach (map {ucfirst lc } sort keys %ENV);

将格式与函数分离的更清晰、因而也更好的代码是:

使用 map,并且除首字母大写外、以小写字母打印并排序所有环境变量的多行循环 foreach (map {ucfirst lc } sort keys %ENV)
{
print $_, "\n"
}

grep 与 map 的工作方式十分类似,只是在表达式或块返回 true 时,元素只是“通过”。map 允许所有元素通过。可以使用 grep 来抽取每一个是有效文件的数组元素,例如:

过滤文件名数组以检索有效性 @valid = grep { -f } @names;

只有在文件存在时,-f 操作符才返回 true。

何时使用模块
如果发现有多个程序使用一个特定函数,则需要将此函数放入模块。不必在模块中使用面向对象的方法。简单地说:

使用 Exporter 模块 package My::Package;

require Exporter;

@ISA = qw(Exporter);

@EXPORT = qw(my_function); # symbols to export by default

sub my_function()
{
}

1;

就足以创建模块 My::Package,并从中导出函数 my_function。现在,当其它程序声明 "use My::Package" 时,它们将自动导入函数 "my_function"。

关于模块还有很多事情要讲。请阅读 perlmod 文档,并参考本文的参考资料一节。强烈建议您学习模块,并试着使用它们。它们将使您的工作更轻松。总的说来,如果在多个程序中使用一个函数,则该函数应该在模块中。

面向对象编程和其它不良习惯
面向对象编程 (OOP) 是一种极好的工具。但是,不要将它看成圣典。OOP 不能:

解决现实世界的问题
在项目中途缩短开发时间
需要昂贵工具
使计划变得没有必要,因为“对象可以照顾自己”
OOP 可以:

在正确使用时使工作更容易
使抽象概念快速成型
与 Perl 模块实现良好接口
简而言之,可以使用 OOP,但不要依赖它做每件事。有关 Perl 中的 OOP 介绍,请参阅参考资料和 perltoot 页面。OOP 可以象数据抽象那样简单,也可以象公司范围的方法学那样复杂。

可以在 PSI::ESP 模块 ESP.pm 中找到简单对象的示例。

ptkdb 和其它相关工具
尽快从 CPAN 获得 ptkdb(请参阅参考资料)。它需要几个模块,但是还是很值得。它将允许您查看程序和装入模块中的数据和代码。tkdb 多次使我免受几小时的挫折和过早脱发。

PSI::ESP 模块
在 Perl 社区中,PSI::ESP 模块很有名。不幸的是,只有少数人真正领会它。其余不够聪明的人只能尽力互相帮助。如果您需要 Perl 问题或 Perl 风格的帮助,请记住在求助信息中包括完整的情况描述、所做的尝试和完整代码。

我将提供一个 ESP 模块的预版本。只要地球磁场还正确对齐,它就会解决您可能遇到的所有 Perl 问题。希望您觉得它有用。

只要将模块放在当前目录的 PSI 子目录中,就可以用以下语法使用该模块:ESP.pm:
perl -I. -MPSI::ESP -e 'use PSI::ESP; $p = new PSI::ESP; print $p->reason'

结束语
希望本文能够帮助您改进一些技巧。这里所讲的大部分信息都可以由参考资料中的信息补充。享受 Perl 的乐趣吧。希望您觉得这是一个增长见识和令人兴奋的固定资源库!

参考资料

reverser.pl 包含本文提到脚本的最后版本,带有代码和注释
可以在 PSI::ESP 模块中找到一个简单对象的例子
其它信息所在的 Web 站点:

要访问可获得实际操作信息的 Perl FAQ,请从 Perl.com 或 Perl.org 开始
CPAN(综合 Perl 档案网络)包含您想要的所有 Perl 模块
请参阅 CPAN 中的 Perl 并行模块
EZ Perl 提供大量用于数据库管理的 Perl 数据库脚本
免费 Perl 代码 提供“大量”免费 Perl 代码
Effective Perl Programming 中有很多有用信息
有关分析英语文本可读性的模块的详细信息,请参阅 Lingua::EN::Fathom 文档
Perl 艺术许可证
Perl 强大工具:Unix 重建项目
Roth 咨询 Perl 页面
相关读物:

Joseph Hall 和 Randal Schwartz 所著的 Effective Perl Programming (Addison Wesley, 1998) 是关于语言(而不是特定任务)的 Perl 技巧和窍门的权威书籍
Damian Conway 所著的 Object Oriented Perl (Manning Publications, 2000) 是一本有关模块和面向对象的极好指南
Larry Wall、Tom Christiansen 和 Randal L. Schwartz 所著的 Programming Perl, 2nd Edition (O'Reilly, 1996) 是现今最好的 Perl 指南,但是与现在的 5.005 和 5.6.0 相比有些过时
Perl 月刊
Perl 新闻组:

USENET comp.lang.perl.misc 新闻组是一个很好的学习去处。在发公告之前,请先读一会儿文章,查看 FAQ,在参与 c.l.p.misc 社区时,要表现良好。请特别看一下第 9 小节。
Perl 会议:

Perl 2000 年会议
关于作者
Teodor Zlatanov 于 1999 年毕业于波士顿大学,并获得计算机工程硕士学位。他从 1992 年成为程序员,并开始用 Perl、Java、C 和 C++ 编程。他的兴趣在于文本语法分析、三层客户机-服务器数据库体系结构、UNIX 系统管理、CORBA 和项目管理方面的开放源码工作。可通过 tzz@bu.edu 与他联系。

摘自:IBM developerWorks中国网站

分享到:
评论

相关推荐

    perl论坛程序CCB v20110627 繁体中文 UTF8.zip

    久别的CCB Perl论坛,4年之后,又和大家见面了,希望作为一个曾经的经典之作改进版,供大家收藏! 2011-6-20版对CCB论坛的程序结构和数据算法进行了大量的优化,目前已是国内现存的非常少见的很稳定成熟的文本系统. 程序...

    NotePad2 MOD-v1.1.1.2 (简/繁/英)

    主目录下的是简/繁/英三语言一体的版本,自动根据系统选择语言,子目录下分别对应各语系的版本。 替换系统记事本可用压缩包中的“替换系统记事本.bat”,还原系统记事本可用压缩包中的“还原系统记事本.bat”。 ...

    实战Linux Shell编程与服务器管理-作者:卧龙小三(7)

    由简入繁、循序渐进,建立扎实的Bash Shell程序设计基础;各章提供许多范例,充分展示Bash Shell程序设计的技巧;带领读者学习如何设计自动化程序,轻松解决问题,增进工作效率;还包含许多管理实务的技巧,可快速...

    bugzilla4.2.2在win7和XP上的安装

    如果需要简体中文版本,可以通过在线工具如“火星文转换器”、“繁简深度转换”或 WPS 进行转换。 ##### 2. ActivePerl(v5.10.0) - **简介**:ActivePerl 是一款运行在 Windows 平台上的 Perl 解释器,用于执行 ...

    winscp.rar

    WinSCP是一款专为Windows用户设计的开放源代码文件传输程序,它主要支持SSH(Secure SHell)协议,同时也兼容SCP(Secure Copy)协议。这款工具以其直观的图形化界面和强大的功能,在IT行业中广泛用于在本地计算机和...

    JAVA上百实例源码以及开源项目源代码

    原理是初始化颜色选择按钮,然后为颜色选择按钮增加事件处理事件,最后实例化颜色选择器。 Java二进制IO类与文件复制操作实例 16个目标文件 内容索引:Java源码,初学实例,二进制,文件复制 Java二进制IO类与文件复制...

    pblang(多国语言)

    作为一款基于PHP编程语言开发的源码,PBLang为开发者和网站管理员提供了高度自定义和扩展的平台,以构建自己的在线社区。 首先,我们需要了解PHP。PHP(Hypertext Preprocessor,超文本预处理器)是一种广泛使用的...

    优秀代码编辑器工具 PhpDesigner 8.1.2 Portable 绿色中文免费版.zip

    3.智能化语法加亮功能PHP, HTML, CSS, JavaScript内自动切换语法加亮区域,这取决于鼠标在文档内的位置同时还淡化剩下的代码,这样你就更容易保持注意力集中—-这十分适合Ajax开发者。 4.php内的面向对象编程PHP内可...

    java开源包1

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

    java开源包11

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

    java开源包2

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

    java开源包3

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

    java开源包6

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

    java开源包5

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

    java开源包10

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

    java开源包4

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

    java开源包8

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

    java开源包7

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

    java开源包9

    nfs-rpc是一个集成了各种知名通信框架的高性能RPC框架,目前其最好的性能为在采用grizzly作为通信框架,采用pb作为序列化/反序列化时,tps为168k次/秒。 其支持的功能主要为: 1、透明的调用远端服务器提供的功能...

Global site tag (gtag.js) - Google Analytics