23

我已经编写 Perl 好几年了,它是我首选的文本处理语言(我研究的许多遗传学/基因组学问题很容易归结为文本处理问题)。Perl 作为一门语言可以是非常宽容的,并且可以用 Perl 编写非常糟糕但实用的代码。就在前几天,我的朋友说他称 Perl 为只写语言:编写一次,理解一次,完成后永远不要尝试返回并修复它。

虽然有时我确实为编写糟糕的脚本而感到内疚,但我觉得我也在 Perl 中编写了一些非常清晰且可维护的代码。但是,如果有人问我是什么代码清晰和可维护,我将无法给出自信的答案。

是什么让 Perl 代码可维护?或者更好的问题是是什么让 Perl 代码难以维护?假设我不是唯一维护代码的人,其他贡献者,像我一样,不是专业的 Perl 程序员,而是具有编程经验的科学家。

4

8 回答 8

33

是什么让 Perl 代码无法维护?几乎任何使任何其他程序都无法维护的东西。假设除了旨在执行明确定义的任务的简短脚本之外的任何其他内容,这些是:

  • 全局变量
  • 缺乏关注点分离:单体脚本
  • 不使用自记录标识符(变量名和方法名)。例如,您应该从变量的名称中知道变量的用途。$c坏的。$count更好的。$token_count好的。
    • 拼出标识符。程序大小不再是最重要的问题。
    • 调用的子例程或方法doWork没有说明任何内容
    • 使从另一个包中查找符号的来源变得容易。要么使用显式包前缀,要么显式导入通过use MyModule qw(list of imports).
  • Perl 特定:
    • 过度依赖捷径和晦涩的内置变量
    • 滥用子程序原型
    • 不使用strict和不使用warnings
  • 重新发明轮子,而不是使用已建立的库
  • 不使用一致的缩进样式
  • 不使用水平和垂直空白来引导读者

等等等等等等

基本上,如果您认为 Perl 是-f>@+?*<.-&'_:$#/%!,并且您渴望在生产代码中编写类似的东西,那么,是的,您将遇到问题。

人们倾向于将 Perl 程序员为了好玩而做的事情(例如,JAPHs、高尔夫等)与好的 Perl 程序应该是什么样子相混淆。

我仍然不清楚他们如何能够将为IOCCC编写的代码与可维护的 C分开。

于 2010-12-03T19:35:14.803 回答
15

我建议:

  1. 不要太聪明地使用 Perl。如果您开始使用代码打高尔夫球,则会导致代码难以阅读。您编写的代码需要可读性和清晰性,而不是聪明。
  2. 记录代码。如果是模块,请添加描述典型用法和方法的 POD。如果是程序,请添加 POD 来描述命令行选项和典型用法。如果有一个毛茸茸的算法,请将其记录下来并尽可能提供参考(URL)。
  3. 使用 /.../x 形式的正则表达式,并记录它们。不是每个人都能很好地理解正则表达式。
  4. 了解什么是耦合,以及高/低耦合的优缺点。
  5. 了解什么是凝聚力,以及高/低凝聚力的利弊。
  6. 适当地使用模块。一个定义明确、包含良好的概念是一个很棒的模块。目标是重用这些模块。不要简单地使用模块来减小单体程序的大小。
  7. 为您的代码编写单元测试。一个好的测试套件不仅可以让你证明你的代码今天可以工作,而且明天也可以。它还可以让您在未来做出更大胆的改变,并确信您不会破坏旧的应用程序。如果您确实破坏了某些东西,那么,那么,您的测试套件还不够广泛。

但总的来说,您足够关心可维护性并提出问题这一事实告诉我,您已经处于一个好的位置并且正在思考正确的方式。

于 2010-12-03T19:59:04.273 回答
13

我没有使用所有Perl Best Practices,但这就是 Damian 编写它的目的。无论我是否使用所有建议,它们都值得至少考虑。

于 2010-12-03T23:03:58.030 回答
9

是什么让 Perl 代码可维护?

至少:

use strict;
use warnings;

请参阅perldoc perlstyle以获得一些使您的程序更易于阅读、理解和维护的通用指南。

于 2010-12-03T19:34:47.343 回答
5

我在其他答案中没有提到的对代码可读性非常重要的一个因素是空格的重要性,它与 Perl 无关,在某些方面与 Perl 相关。

Perl 允许您编写非常简洁的代码,但简洁的块并不意味着它们必须全部捆绑在一起。

当我们谈论可读性时,空白有很多含义/用途,并非所有这些都被广泛使用但最有用:

  • 标记周围的空间可以更容易地在视觉上将它们分开。

    由于行噪声字符的普遍存在,即使在最佳风格的 Perl 代码中,这个空间在 Perl 中也很重要。

    我发现$myHashRef->{$keys1[$i]}{$keys3{$k}}在生产紧急情况下凌晨 2 点的可读性低于间隔: $myHashRef->{ $keys1[$i] }->{ $keys3{$k} }.

    作为旁注,如果您发现您的代码执行了很多深度嵌套引用表达式,都以相同的根开头,那么您绝对应该考虑将该根分配给一个临时指针(参见 Sinan 的评论/答案)。

    一个部分但非常重要的特殊情况当然是正则表达式。我记得的所有主要材料(PBP、RegEx O'Reilly 书等)都说明了这种差异,所以我不会进一步延长这篇文章,除非有人在评论中要求示例。

  • 正确和统一的压痕。哦。明显地。然而,由于糟糕的缩进,我看到太多代码 100% 不可读,而且当一半代码由一个编辑器使用 4 个字符选项卡的人使用 TAB 缩进,另一个由一个编辑器使用 8 个字符 TAB 的人缩进时,可读性更差。只需将你的编辑器设置为软(例如空间模拟)TAB,不要让其他人痛苦。

  • 逻辑上独立的代码单元(块和行集)周围的空行。你可以用 1000 行好的 Perl 编写一个 10000 行的 Java 程序。现在,如果您在这 1000 行中添加 100-200 个空行以使内容更具可读性,那么现在就不会觉得像 Benedict Arnold。

  • 将超长表达式拆分为多行,紧随其后的是...

  • 正确的垂直对齐。见证两者之间的区别:

    if ($some_variable > 11 && ($some_other_bigexpression < $another_variable || $my_flag eq "Y") && $this_is_too_bloody_wide == 1 && $ace > my_func() && $another_answer == 42 && $pi == 3) {
    

    if ($some_variable > 11 && ($some_other_bigexpression < $another_variable || 
        $my_flag eq "Y") && $this_is_too_bloody_wide == 1 && $ace > my_func()
        && $another_answer == 42 && $pi == 3) {
    

    if (   $some_variable > 11
        && ($some_other_bigexpression < $another_variable || $my_flag eq "Y")
        && $this_is_too_bloody_wide == 1
        && $ace > my_func()
        && $another_answer == 42
        && $pi == 3) {
    

    就个人而言,我更喜欢通过对齐 LHS 和 RHS 来进一步修复垂直对齐(这在长 SQL 查询的情况下尤其可读,但在 Perl 代码本身中,像这样的长条件以及许多行的赋值和哈希/数组初始化):

    if (   $some_variable               >  11
        && ($some_other_bigexpression   <  $another_variable || $my_flag eq "Y")
        && $this_is_too_bloody_wide    ==  1
        && $ace                         >  my_func()
        && $another_answer             ==  42
        && $pi                         ==  3  ) {
    

    附带说明一下,在某些情况下,如果一开始就没有这么长的表达式,可以使代码更具可读性/可维护性。例如,如果if(){}块的内容是 a return,那么执行多个if/unless语句,每个语句都有一个返回块可能会更好。

于 2010-12-04T02:46:24.060 回答
4

我认为这是人们被告知 perl 不可读的问题,他们开始对自己代码的可维护性做出假设。如果你足够认真地认为可读性是高质量代码的标志,那么这种批评很可能不适用于你。

大多数人在讨论可读性时会引用正则表达式。正则表达式是嵌入在 perl 中的 dsl,您可以阅读或不阅读它们。如果有人不能花时间去理解对许多语言来说如此基本和必不可少的东西,我不担心试图弥合一些推断的认知差距……他们应该站起来,阅读 perldocs,并在必要时提出问题.

其他人会引用 perl 对短格式变量的使用,例如 @_, $! 等等这些都很容易消除歧义......我对让 perl 看起来像 java 不感兴趣。

所有这些怪癖和偏见的好处是用该语言编写的代码库通常简洁紧凑。我宁愿读十行 perl 而不是读一百行 java。

对我来说,“可维护性”不仅仅是拥有易于阅读的代码。编写测试,做出断言……尽你所能依靠 perl 及其生态系统来保持代码正确。

简而言之:编写程序首先是正确的,然后是安全的,然后是性能良好的......一旦这些目标实现了,然后担心在火附近蜷缩起来很舒服。

于 2010-12-03T21:40:26.410 回答
2

我会说包装/对象模型,它反映在 .pm 文件的目录结构中。在攻读博士学位时,我写了很多 Perl 代码,之后我会重复使用这些代码。它用于自动 LaTeX 图表生成器。

于 2010-12-03T19:31:06.493 回答
1

我将谈论一些积极的事情来使 Perl 可维护。

确实,您通常不应该对真正密集的语句 a lareturn !$@;#%之类的过于聪明,但是要巧妙地使用列表处理运算符,例如mapandgrep和 list-context 从 likesplit和类似的运算符返回,以便以函数式风格编写代码可以对可维护性做出积极贡献。在我的上一个雇主中,我们也有一些时髦的哈希操作函数,它们以类似的方式工作(虽然从技术上讲hashmaphashgrep我们只为它们提供了偶数大小的列表)。例如:

# Look for all the servers, and return them in a pipe-separated string
# (because we want this for some lame reason or another)
return join '|', 
       sort
       hashmap {$a =~ /^server_/ ? $b : +()} 
       %configuration_hash;

另请参阅Higher Order Perlhttp ://hop.perl.plover.com - 如果您可以防止元编程本身妨碍您,那么充分利用元编程可以使定义任务更加连贯和可读。

于 2012-07-05T23:23:21.837 回答