我们应该雇用一个在Perl中编写C的人吗?

我的一个同事最近采访了一些求职者,一个人说他们有很好的Perl经验。

由于我的同事不认识Perl,他要求我批评一下这个潜在聘请的一些代码(不在现场),所以我看了看,并告诉他我的疑虑(主要的原因是它最初没有评论并不像我们给他们足够的时间)。

然而,代码的作品,所以我不喜欢没有更多的投入说不走。 另一个值得关注的问题是,这段代码基本上看起来完全是用C语言编写的。从我做了Perl开始,我已经有一段时间了(而且我没有做太多的事情,我更像是一个Python脚本)回想一下,这是一个比这个人使用的更具performance力的语言。

我正在寻找真正的Perl编码人员的意见,以及如何改进的build议(以及为什么Perl编码人员应该知道这种改进方法)。

你也可以用一种完全不同的语言写一种语言的人(或者不应该雇用)来expression自己的意思。 我对你的论点感兴趣,但是这个问题主要是对代码的批评。

规范是成功处理一个CSV文件如下,并输出个别字段:

User ID,Name , Level,Numeric ID pax, Pax Morgan ,admin,0 gt," Turner, George" rubbish,user,1 ms,"Mark \"X-Men\" Spencer","guest user",2 ab,, "user","3" 

输出是这样的(潜在的雇佣代码实际上输出这个):

 User ID,Name , Level,Numeric ID: [User ID] [Name] [Level] [Numeric ID] pax, Pax Morgan ,admin,0: [pax] [Pax Morgan] [admin] [0] gt," Turner, George " rubbish,user,1: [gt] [ Turner, George ] [user] [1] ms,"Mark \"X-Men\" Spencer","guest user",2: [ms] [Mark "X-Men" Spencer] [guest user] [2] ab,, "user","3": [ab] [] [user] [3] 

这是他们提交的代码:

 #!/usr/bin/perl # Open file. open (IN, "qq.in") || die "Cannot open qq.in"; # Process every line. while (<IN>) { chomp; $line = $_; print "$line:\n"; # Process every field in line. while ($line ne "") { # Skip spaces and start with empty field. if (substr ($line,0,1) eq " ") { $line = substr ($line,1); next; } $field = ""; $minlen = 0; # Detect quoted field or otherwise. if (substr ($line,0,1) eq "\"") { $line = substr ($line,1); $pastquote = 0; while ($line ne "") { # Special handling for quotes (\\ and \"). if (length ($line) >= 2) { if (substr ($line,0,2) eq "\\\"") { $field = $field . "\""; $line = substr ($line,2); next; } if (substr ($line,0,2) eq "\\\\") { $field = $field . "\\"; $line = substr ($line,2); next; } } # Detect closing quote. if (($pastquote == 0) && (substr ($line,0,1) eq "\"")) { $pastquote = 1; $line = substr ($line,1); $minlen = length ($field); next; } # Only worry about comma if past closing quote. if (($pastquote == 1) && (substr ($line,0,1) eq ",")) { $line = substr ($line,1); last; } $field = $field . substr ($line,0,1); $line = substr ($line,1); } } else { while ($line ne "") { if (substr ($line,0,1) eq ",") { $line = substr ($line,1); last; } if ($pastquote == 0) { $field = $field . substr ($line,0,1); } $line = substr ($line,1); } } # Strip trailing space. while ($field ne "") { if (length ($field) == $minlen) { last; } if (substr ($field,length ($field)-1,1) eq " ") { $field = substr ($field,0, length ($field)-1); next; } last; } print " [$field]\n"; } } close (IN); 

我build议人们不要雇用Perl程序员,C程序员或Java程序员等等。 只雇用好人。 我聘请编写Perl的程序员也熟练使用其他各种语言。 我雇用他们是因为他们是好的程序员,好的程序员可以处理多种语言。

现在,这个代码看起来很像C,但我认为它也是很好的Perl。 如果你正在招聘一名优秀的程序员,在他的皮带下练习一下Perl,他就会很好。 人们抱怨缺less正则expression式,这会使辅助领域的事情变得更简单,但是我不希望任何人都能parsing那些脏的CSV数据。 我不想阅读或维护它。

我经常发现相反的问题更麻烦:聘请一个编写好Perl代码的好程序员,但是其他人只知道Perl的基本知识,并且跟不上。 这与糟糕的格式化或糟糕的结构无关,仅仅是具有高级主题(例如closures)的技能水平。


在这场辩论中,事情变得有些激烈,所以我想我应该多解释一下我如何处理这类事情。 我不认为这是一个正则expression式/没有正则expression式的问题。 我不会像候选人那样写代码,但这并不重要。

我写了很多蹩脚的代码。 在第一遍中,我通常比语法更多地考虑结构和过程。 我稍后再回来收紧。 这并不意味着候选人的守则是好的,但是面试中的第一次传球我并不太苛刻。 我不知道他写了多less时间等等,所以我不会根据我将要花费很长时间去做的事来判断它。 面试问题总是很奇怪,因为你不能做真正的工作。 如果我必须从头开始并在15分钟内完成,那么我也可能会失败一个关于编写CSVparsing器的问题。 的确,我今天浪费了比今天更多的代码。

我去看Text :: CSV_PP的代码,Pure Perl的表弟到Text :: CSV_XS 。 它使用正则expression式,但是处理特殊情况的正则expression式很多,并且在结构上与这里给出的代码没有什么不同。 这是很多代码,而且它是复杂的代码,我希望我再也不用再看了。

我倾向于不喜欢的采访答案只涉及给定的input。 在现实世界中,你必须处理你可能还没有发现的情况,而且你需要灵活处理未来的问题,这几乎总是一件错误的事情。 我发现从Stackoverflow的很多答案也没有。 解决scheme的思维过程更能告诉我。 人们对语言的熟练程度要比改变他们对事物的看法更容易。 我可以教人们如何编写更好的Perl,但我不能改变他们的湿件大部分。 这是来自伤痕和经验。

由于我不是在那里看候选人的解决scheme,或者问他后续的问题,我不会推测他为什么这么写。 对于我在这里看到的其他一些解决scheme,在采访中我可能同样苛刻。

职业是一个旅程。 我不指望每个人都是上师,或者有相同的经历。 如果我因为不知道一些诀窍或习惯用语而注销了人,我不会给他们继续旅行的机会。 候选人的代码将不会赢得任何奖项,但显然这足以让他进入最后三个考虑要约。 那家伙起身去试了一下,比我见过的很多代码都好多了,这对我来说已经足够了。

他的代码有点冗长。 Perl是关于模块的,而避免它们会让你的生活变得艰难。 这是一个相当于你在两分钟内写的东西:

  #!/usr/bin/env perl use strict; use warnings; use Text::CSV; my $parser = Text::CSV->new({ allow_whitespace => 1, escape_char => '\\', allow_loose_quotes => 1, }); while(my $line = <>){ $parser->parse($line) or die "Parse error: ". $parser->error_diag; my @row = $parser->fields; print $line; print "\t[$_]\n" for @row; } 

我认为在Perl中编写C语言比在Perl中编写Perl要好得多。正如通常在SO播客中提到的那样,理解C是一种优点,并不是所有的开发者(即使是一些好的开发者)现在都有。 雇用他们,并为他们购买Perl最佳实践的副本,你将被设置。 经过最佳实践中级Perl的副本,他们可以解决。

这不是可怕的惯用的Perl,但它不是完全可怕的Perl(虽然它可以更紧凑)。

两个警告铃 – shebang线不包括' -w ',既没有' use strict; “也”不use warnings; ”。 这是非常古老的Perl; 好的Perl代码使用警告和严格。

不再推荐使用旧式的文件句柄,但它不会自动变坏(可能是10年前写的代码)。

不使用正则expression式更令人惊讶。 例如:

 # Process every field in line. while ($line ne "") { # Skip spaces and start with empty field. if (substr ($line,0,1) eq " ") { $line = substr ($line,1); next; } 

这可以写成:

 while ($line ne "") { $line =~ s/^\s+//; 

这使用正则expression式排除所有前导空格,而不会使代码绕着循环迭代。 其他代码中的大部分代码也会受益于精心编写的正则expression式。 这是一个有特色的Perl成语; 看到他们没有被使用是令人惊讶的。

如果效率是被宣布的担心(不使用正则expression式的原因),那么问题应该是“你测量了它”和“你讨论的是什么样的效率 – 机器或程序员”?

工作代码计数。 或多或less的地道代码更好。

当然,还有一些模块Text :: CSV和Text :: CSV_XS可以用来处理CSVparsing。 询问他们是否知道Perl模块会很有趣。


引用字段中还有多个用于处理引号的符号。 代码似乎假定反斜杠引用是适当的; 我相信Excel使用双引号:

 "He said, ""Don't do it"", but they didn't listen" 

这可以匹配:

 $line =~ /^"([^"]|"")*"/; 

仔细一点,你可以捕获封闭引号之间的文本。 您仍然必须对捕获的文本进行后期处理,以删除embedded的加倍引号。

一个非引用的字段将会被匹配:

 $line =~ /^([^,]*)(?:,|$)/; 

这比所示的循环和子串非常短。


下面是代码的一个版本,使用问题代码中使用的反斜杠双引号转义机制来完成相同的工作。

 #!/usr/bin/perl -w use strict; open (IN, "qq.in") || die "Cannot open qq.in"; while (my $line = <IN>) { chomp $line; print "$line\n"; while ($line ne "") { $line =~ s/^\s+//; my $field = ""; if ($line =~ m/^"((?:[^"]|\\.)*)"([^,]*)(?:,|$)/) { # Quoted field $field = "$1$2"; $line = substr($line, length($field)+2); $field =~ s/""/"/g; } elsif ($line =~ m/^([^,]*)(?:,|$)/) { # Unquoted field $field = "$1"; $line = substr($line, length($field)); } else { print "WTF?? ($line)\n"; } $line =~ s/^,//; print " [$field]\n"; } } close (IN); 

这是30个非空白,非评论线,比原来的约70。 原来的版本比它需要的更大一些。 而且我还没有竭尽全力将代码降到最低。

没有使用严格/使用警告,系统使用substr而不是正则expression式,不使用模块。 这绝对不是有“ 非常好的Perl体验 ”的人。 至less不是现实生活中的Perl项目。 和你一样,我怀疑这可能是一个具有Perl基础知识的C程序员。

这并不意味着他们无法学习,尤其是因为还有其他的Perl人。 这似乎意味着他们夸大了他们的工作资格。 还有几个关于他们如何获得非常好的Perl体验的问题

我不在乎他是否使用正则expression式。 我也不在乎他的Perl看起来像C还是不是。 真正重要的问题是:这是好的Perl吗? 我想说这不是:

  1. 他没有use strict
  2. 他没有启用警告。
  3. 他正在使用open的老式双参数版本。
  4. “打开文件”评论伤害,给我的印象是,他通常写的代码不包含任何评论。
  5. 代码很难维护
  6. 他允许使用CPAN模块吗? 一个好的Perl程序员会先看看这个选项。

我必须(有点)不同意在这里expression的大多数意见。

由于有问题的代码可以用惯用的Perl来expression得更加紧凑和可维护,所以你真的需要提出这个问题:候选人花费多less时间来开发这个解决scheme,以及那些半熟练使用惯用Perl的人花了多less时间。

我想你会发现这种编码风格可能会浪费很多时间(从而也是公司的资金)。

我并不认为每个Perl程序员都需要对语言进行研究 – 不幸的是,这种语言将会是牵强的 – 但是他们应该知道,不要花费不less时间重复在代码中重复实现核心语言特性。

编辑再次看看代码,我必须更激烈:虽然代码看起来很干净,但实际上是可怕的 。 抱歉。 这不是Perl。 你是否知道“你可以用任何语言编程Fortran”? 是的你可以。 但是你不应该。

这是你需要跟进程序员的情况。 问他为什么这样写。

可能有一个很好的理由..也许这需要遵循现有的代码相同的行为,因此,他为了完全兼容性做了一行一行的翻译。 如果是这样,给他点一个体面的解释。

或者也许他不认识Perl,那么下午他就学会了回答这个问题。 如果是的话,给他点快速和灵活的学习技巧。

唯一不合格的评论可能是“我总是这样编程Perl,我不明白正则expression式的东西。”

我会说他的代码是一个适当的解决scheme。 它有效,不是吗? 通过编写“longhand”而不是尽可能less的代码字符,可维护性是有好处的。

Perl的座右铭是“ 不止一个方法去做 ”。 Perl并没有像编程风格那样真正理解你的情况,因为有些语言(我也喜欢Python),但是你必须承认,在评估代码是否是pythonic时,人们可能会产生势利。

它工作吗? 他在一段可以接受的时间写了吗? 你认为这是可维护的吗?

如果你能回答我这三个问题,他们可以通过死亡之桥( * )。

我的一个同事最近采访了一些求职者,一个人说他们有很好的Perl经验。

如果这个人认为他有很好的Perl经验,并且他这样写Perl,那么他可能是Dunning-Kruger效应的受害者。

所以,这是一个没有雇用。

我认为最大的问题是他或她没有显示任何正则expression式的知识。 这是Perl的关键。

问题是,他们可以学习吗? 在这段代码之后的候选人中有很多东西需要注意。

我不会接受这个候选人。 他或她对Perl的习语不太熟悉,这会导致代码不完整,工作效率降低(所有这些不必要的行都必须写出来),而且不能读懂由有经验的Perl编码人员编写的代码(当然,他们使用正则expression式等等)。

但它工作…

原谅这个人。 即使可以完成,我也不敢用正则expression式parsingCSV。

结构化代码中的DFA比这里的正则expression式更加明显,DFA – >正则expression式翻译不平凡,容易出现愚蠢的错误。

只是最初的块表示他已经错过了有关Perl的基础知识。

  while ($line ne "") { # Skip spaces and start with empty field. if (substr ($line,0,1) eq " ") { $line = substr ($line,1); next; } 

至less应该使用正则expression式来删除前导空格。 我最喜欢jrockway的答案 ,模块摇滚。 虽然我会用正则expression式来做这件事。

 #!/usr/bin/perl -w # # $Id$ # use strict; open(FD, "< qq.in") || die "Failed to open file."; while (my $line = <FD>) { # Don't like chomp. $line =~ s/(\r|\n)//g; # ".*?[^\\\\]" = Match everything between quotations that doesn't end with # an escaped quotation, match lazy so we will match the shortest possible. # [^",]*? = Match strings that doesn't have any quotations. # If we combine the two above we can match strings that contains quotations # anywhere in the string (or doesn't contain quotations at all). # Put them together and match lazy again so we can match white-spaces # and don't include them in the result. my $match_field = '\s*((".*?[^\\\\]"|[^",]*?)*)\s*'; if (not $line =~ /^$match_field,$match_field,$match_field,$match_field$/) { die "Invalid line: $line"; } # Put values in nice variables so we don't have to deal with cryptic $N # (and can use $1 in replace). my ($user_id, $name, $level, $numeric_id) = ($1, $3, $5, $7); print "$line\n"; for my $field ($user_id, $name, $level, $numeric_id) { # If the field starts with a quotation, # strip everything after the first unescaped quotation. $field =~ s/^"(.*?[^\\\\])".*/$1/g; # Now fix all escaped variables (not only quotations). $field =~ s/\\(.)/$1/g; print " [$field]\n"; } } close FD; 

他没有在代码中使用一个正则expression式的事实应该让你问他为什么写这样的问题很多问题。

也许他是Jamie Zawinski或粉丝,他不想有更多的问题?

我不一定说整个parsing应该是大量的不可读的CSVparsing正则expression式,如("([^"]*|"{2})*"(,|$))|"[^"]*"(,|$)|[^,]+(,|$)|(,)或者其中一个相似的正则expression式,但至less要遍历行或者使用substring()

也许问他写更多版本的相同的代码? 如果对招聘有疑问,请向候选人提问。

代码不仅表明候选人并不真正了解Perl,而且所有那些说$line = substr ($line,1)任何语言中都是可怕的。 尝试使用这种types的方法parsing一个长行(比如说几千个字段),你会看到为什么。 这只是表明了Joel Spolsky在这篇文章中讨论的问题。

一个显而易见的问题可能是,如果你的公司首先不使用Perl,那么他的Perl代码有多漂亮呢?

我不确定他的Perl代码的优雅是否与你使用的任何语言有关。

作为一个非Perl(?程序员?),我不得不说,这可能是我读过的最清晰的Perl! 🙂

雇用某些人,比如可以在几天到几周内学会的脚本语言(如果这是一种有价值的脚本语言!),似乎是非常有缺陷的。

我个人可能会雇用这个人出于不同的原因。 代码结构合理,评论合理。 以后可以很容易地教授语言细节。

这里至关重要的一点是 – 在确保代码能够正常工作之后,自然就能够维护代码。

  • 明白吗?
  • 你会觉得很容易修复它的错误?

Perl程序有一种趋势,就是在键盘上行走时看起来像是一只偶然发生的types的猫。 如果这个人知道如何编写适合团队的可读Perl代码,这实际上是一件好事。

然后再一次,你可能想教他正则expression式,但只有小心:-)

代码看起来干净可读。 对于这个规模来说,并不需要太多的评论(可能根本就没有)。这不仅仅是好评,还有好的代码,后者比前者更重要。

如果我们正在查看更复杂/更大的代码段,我会说需要注释。 但是对于that (特别是写的方式 – 写得很好),我不这么认为。

如果他/她提交的这段代码是完全可以接受的并且做了这项工作,我认为对申请人产生怀疑是不公平和徒劳的。

嗯,我没有看到任何请求中的引号应该删除,单词应该被删除。 input文件有“垃圾”这个词,它不在输出中。

我已经看到CSV文件,用引号导出,会期望返回相同的引号。 如果你的规范是删除引号和过去的引号外的话,也许这项工作将是必需的。

我会看,那个冗长。 找一个懒惰的人(在Perl中称赞)。

 open (IN, "csv.csv"); while (<IN>) { #print $_; chomp; @array = split(/,/,$_); print "[User Id] = $array[0] [Name] = $array[1] [Level] = $array[2] [Numeric ID] = $array[3]\n"; }