我的一位同事最近采访了一些求职者,其中一位表示他们有非常好的Perl经验.
由于我的同事不认识Perl,他要求我批评一些潜在雇员编写的(异地)代码,所以我看了一眼并告诉他我的担忧(主要的是它最初没有评论)而且它不像我们给他们足够的时间).
但是,代码有效,所以我不愿意在没有更多输入的情况下说不行.另一个问题是,这段代码基本上看起来就像我在C中编写代码一样.自从我做Perl以来已经有一段时间了(我没有做很多事情,我更喜欢Python脚本用于快速脚本)但我似乎要记住,这是一个比这个人使用的更具表现力的语言.
我正在寻找真正的Perl编码器的输入,以及如何改进它的建议(以及为什么Perl编码器应该知道改进方法).
你也可以抒情地说,是否应该(或不应该雇用)以完全不同的语言写一种语言的人.我对你的论点很感兴趣,但这个问题主要是对代码的批评.
规范是按如下方式成功处理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 () { 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);
brian d foy.. 165
我建议人们永远不要雇用Perl程序员,C程序员或Java程序员,等等.只是雇用好人.我聘请编写Perl的程序员也熟练掌握其他各种语言.我雇用他们是因为他们是优秀的程序员,优秀的程序员可以处理多种语言.
现在,该代码确实看起来很像C,但我认为Perl也很好.如果你正在招聘一名优秀的程序员,在他的腰带上进行一些Perl练习,他会很好地追赶.人们抱怨缺乏正则表达式,这会使辅助领域的事情变得更简单,但我不希望任何人在解析那些脏的CSV数据时使用正则表达式解决方案.我不想阅读或维护它.
我经常发现反向问题更麻烦:聘请一位编写好Perl代码的优秀程序员,但团队的其他成员只知道Perl的基础知识并且无法跟上.这与糟糕的格式化或糟糕的结构无关,只与高级主题(例如闭包)的技能水平无关.
在这场辩论中事情变得有点激烈,所以我想我应该更多地解释一下我是如何处理这类事情的.我不认为这是正则表达式/非正则表达式问题.我不会像候选人那样编写代码,但这并不重要.
我也写了很多糟糕的代码.在第一遍,我通常更多地考虑结构和过程而不是语法.我后来回去把它收紧.这并不意味着候选人的代码是好的,但对于在面试中完成的第一次传球我不会过于严厉地判断.我不知道他有多少时间写它等等,所以我不会根据我需要很长时间才能完成的事情来判断它.面试问题总是很奇怪,因为你不能做你真正为实际工作所做的事情.如果我不得不从头开始并在15分钟内完成,我可能也不会有关于编写CSV解析器的问题.事实上,我今天浪费的不仅仅是一些带有一些代码的傻瓜.
我去看了Text :: CSV_PP的代码,Pure Perl表兄到Text :: CSV_XS.它使用正则表达式,但是许多正则表达式处理特殊情况,并且在结构上与此处提供的代码没有什么不同.这是很多代码,它是复杂的代码,我希望我再也不用看了.
我倾向于不喜欢的是面试答案,只能解决给定的输入.在现实世界中,这几乎总是错误的,你必须处理你可能还没有发现的案例,你需要灵活处理未来的问题.我发现Stackoverflow上的很多答案都缺少了.解决方案的思维过程对我来说更有说服力.人们比他们改变对事物的思考方式更容易熟练掌握语言.我可以教人们如何写出更好的Perl,但我不能在大多数情况下更换他们的湿件.这来自于伤疤和经验.
由于我不在那里看候选代码解决方案或问他后续问题,我不会推测为什么他按照他的方式写它.对于我在这里看到的其他一些解决方案,我在采访中也同样苛刻.
事业是一段旅程.我不希望每个人都成为一个大师或拥有相同的经历.如果我因为不知道某些伎俩或成语而注销人,我就不会给他们继续他们旅程的机会.候选人的代码不会赢得任何奖项,但显然足以让他进入最后三个考虑提供奖金.那家伙站起来尝试,比我生命中看到的许多代码做得更好,这对我来说已经足够了.
我建议人们永远不要雇用Perl程序员,C程序员或Java程序员,等等.只是雇用好人.我聘请编写Perl的程序员也熟练掌握其他各种语言.我雇用他们是因为他们是优秀的程序员,优秀的程序员可以处理多种语言.
现在,该代码确实看起来很像C,但我认为Perl也很好.如果你正在招聘一名优秀的程序员,在他的腰带上进行一些Perl练习,他会很好地追赶.人们抱怨缺乏正则表达式,这会使辅助领域的事情变得更简单,但我不希望任何人在解析那些脏的CSV数据时使用正则表达式解决方案.我不想阅读或维护它.
我经常发现反向问题更麻烦:聘请一位编写好Perl代码的优秀程序员,但团队的其他成员只知道Perl的基础知识并且无法跟上.这与糟糕的格式化或糟糕的结构无关,只与高级主题(例如闭包)的技能水平无关.
在这场辩论中事情变得有点激烈,所以我想我应该更多地解释一下我是如何处理这类事情的.我不认为这是正则表达式/非正则表达式问题.我不会像候选人那样编写代码,但这并不重要.
我也写了很多糟糕的代码.在第一遍,我通常更多地考虑结构和过程而不是语法.我后来回去把它收紧.这并不意味着候选人的代码是好的,但对于在面试中完成的第一次传球我不会过于严厉地判断.我不知道他有多少时间写它等等,所以我不会根据我需要很长时间才能完成的事情来判断它.面试问题总是很奇怪,因为你不能做你真正为实际工作所做的事情.如果我不得不从头开始并在15分钟内完成,我可能也不会有关于编写CSV解析器的问题.事实上,我今天浪费的不仅仅是一些带有一些代码的傻瓜.
我去看了Text :: CSV_PP的代码,Pure Perl表兄到Text :: CSV_XS.它使用正则表达式,但是许多正则表达式处理特殊情况,并且在结构上与此处提供的代码没有什么不同.这是很多代码,它是复杂的代码,我希望我再也不用看了.
我倾向于不喜欢的是面试答案,只能解决给定的输入.在现实世界中,这几乎总是错误的,你必须处理你可能还没有发现的案例,你需要灵活处理未来的问题.我发现Stackoverflow上的很多答案都缺少了.解决方案的思维过程对我来说更有说服力.人们比他们改变对事物的思考方式更容易熟练掌握语言.我可以教人们如何写出更好的Perl,但我不能在大多数情况下更换他们的湿件.这来自于伤疤和经验.
由于我不在那里看候选代码解决方案或问他后续问题,我不会推测为什么他按照他的方式写它.对于我在这里看到的其他一些解决方案,我在采访中也同样苛刻.
事业是一段旅程.我不希望每个人都成为一个大师或拥有相同的经历.如果我因为不知道某些伎俩或成语而注销人,我就不会给他们继续他们旅程的机会.候选人的代码不会赢得任何奖项,但显然足以让他进入最后三个考虑提供奖金.那家伙站起来尝试,比我生命中看到的许多代码做得更好,这对我来说已经足够了.
他的代码有点冗长.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语言比在C语言中编写Perl要好得多.正如在SO播客中经常提到的那样,理解C是一种并非现在所有开发人员(甚至是一些好的开发人员)的优点.雇用他们并为他们购买Perl最佳实践的副本,您将被设置.经过最佳实践后,中级Perl的副本可以解决.
它不是可怕的惯用Perl,但它也不是完全可怕的Perl(虽然它可以更加紧凑).
两个警告铃声 - shebang线不包括' -w
',既没有' use strict;
'也没有' use warnings;
'.这是非常老式的Perl; 好的Perl代码同时使用警告和严格.
不再推荐使用旧式文件句柄,但它不会自动坏(可能是10年前编写的代码,也许).
不使用正则表达式更令人惊讶.例如:
# 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+//;
这会使用正则表达式删除所有前导空格,而不会使代码在循环周围迭代.其余的代码也可以从精心编写的正则表达式中受益.这些是特征性的Perl成语; 令人惊讶的是,他们没有被使用.
如果效率是公认的问题(不使用正则表达式的原因),那么问题应该是"你测量它"和"你正在讨论什么样的效率 - 机器或程序员"?
工作代码计数.或多或少的惯用代码更好.
当然,还有模块Text :: CSV和Text :: CSV_XS可用于处理CSV解析.询问他们是否了解Perl模块会很有趣.
在引用字段中还有多个用于处理引号的符号.代码似乎假设反斜杠引用是合适的; 我相信Excel使用加倍的报价:
"He said, ""Don't do it"", but they didn't listen"
这可以匹配:
$line =~ /^"([^"]|"")*"/;
有点小心,你可以只捕获封闭引号之间的文本.您仍然需要对捕获的文本进行后处理以删除嵌入的双引号.
未引用的字段将匹配:
$line =~ /^([^,]*)(?:,|$)/;
这比所示的循环和子串缩短得多.
这是代码的一个版本,使用问题代码中使用的反斜杠双引号转义机制,它执行相同的工作.
#!/usr/bin/perl -w use strict; open (IN, "qq.in") || die "Cannot open qq.in"; while (my $line =) { 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而不是regexp,不使用模块.绝对不是那些拥有" 非常好的Perl体验 "的人.至少不适用于现实生活中的Perl项目.和你一样,我怀疑它可能是一个具有Perl基础知识的C程序员.
这并不意味着他们无法学习,特别是因为周围还有其他Perl人.这似乎意味着他们夸大了他们的工作资格.关于他们如何获得非常好的Perl体验的几个问题将是有序的.
我不在乎他是否使用正则表达式.我也不在乎他的Perl是否看起来像C.真正重要的问题是:这个好Perl?而且我会说它不是:
他没有使用 use strict
他没有启用警告.
他正在使用老式的两个版本的open.
"打开文件"评论会让人觉得他通常写的代码不包含任何评论.
代码很难维护
他被允许使用CPAN模块吗?一个优秀的Perl程序员会先看看这个选项.
我必须(有点)不同意这里表达的大多数观点.
由于有问题的代码可以在惯用的Perl中表达得更加紧凑和易于维护,因此您需要提出一个问题,即候选人花费多少时间来开发此解决方案,以及使用惯用Perl熟练掌握多少时间.
我想你会发现这种编码风格可能会浪费大量时间(因而也就是公司的钱).
我不认为每个Perl程序员都需要理解这种语言 - 遗憾的是,这种语言很难实现 - 但是他们应该足够了解不要花费多年时间在代码中重复实现核心语言功能.
编辑再次查看代码,我必须更加激烈:虽然代码看起来非常干净,但它实际上很糟糕.抱歉.这不是Perl.你知道"你可以用任何语言编写Fortran"的说法吗?是的你可以.但你不应该.
在这种情况下,您需要跟进程序员.问他为什么这样写.
可能有一个很好的理由......也许这需要遵循与现有代码相同的行为,因此他为了完全兼容性而进行了逐行翻译.如果是这样的话,请给他一些好的解释.
或许他不知道Perl,所以他在那天下午学会了回答这个问题.如果是这样的话,给他点快速灵活的学习技巧.
唯一不合格的评论可能是"我总是用这种方式编写Perl.我不明白那个正则表达式的东西."
它有用吗?他是否在可接受的时间内写下了?你认为它是可维护的吗?
如果你能回答我这些问题三,你可以通过死亡之桥(*).
我会说他的代码是一个合适的解决方案.它有效,不是吗?通过编写"longhand"代替尽可能少的代码字符,可维护性具有优势.
Perl的座右铭是" 不止一种方式去做 ".Perl并没有真正了解有关编码风格的案例,就像有些语言一样(我也喜欢Python,但是你必须承认人们在评估代码是否是"pythonic"时)会得到一些势利.
我的一位同事最近采访了一些求职者,其中一位表示他们有非常好的Perl经验.
如果这个人认为他有非常好的Perl经验并且他像这样写Perl,他可能是Dunning-Kruger效应的受害者.
所以,这是一个没有聘用.
我认为最大的问题是他或她没有表现出正则表达式的任何知识.这是Perl的关键.
问题是,他们可以学习吗?在这段代码中,候选人需要寻找很多东西.
我不接受候选人.他或她对Perl的习语感到不舒服,这会导致代码不够理想,工作效率降低(所有那些不必要的行必须写出来!)以及阅读由经验丰富的Perl编码器编写的代码的无法使用(当然使用正则表达式)等等).
但它有效......
只是初始块表明他已经错过了关于Perl的基础知识.
while ($line ne "") { # Skip spaces and start with empty field. if (substr ($line,0,1) eq " ") { $line = substr ($line,1); next; }
至少应使用正则表达式来删除前导空格.我喜欢jrockway最好的答案,模块摇滚.虽然我会用正则表达式来做这件事,比如说.
#!/usr/bin/perl -w # # $Id$ # use strict; open(FD, "< qq.in") || die "Failed to open file."; while (my $line =) { # 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;
原谅这个家伙.即使可以完成,我也不敢用正则表达式解析CSV.
结构化代码中的DFA比这里的正则表达式更明显,DFA - >正则表达式翻译是不平凡的,容易出现愚蠢的错误.