Car*_*icz 4 perl refactoring coding-style
我是一位经验丰富的开发人员,但不是Perl.我通常学习Perl来破解脚本,然后我再次忘记它直到下一次.因此,我正在寻找专业人士的建议.
这一次,我正在构建一系列数据分析脚本.大致简化,程序结构如下:
01 my $config_var = 999;
03 my $result_var = 0;
05 foreach my $file (@files) {
06 open(my $fh, $file);
07 while (<$fh>) {
08 &analyzeLine($_);
09 }
10 }
12 print "$result_var\n";
14 sub analyzeLine ($) {
15 my $line = shift(@_);
16 $result_var = $result_var + calculatedStuff;
17 }
Run Code Online (Sandbox Code Playgroud)
在现实生活中,有大约六个不同的config_vars和result_vars.
这些脚本主要区别于分配给config_vars 的值.主循环在每种情况下analyzeLine()都是相同的,并且将大致相同但可能有一些小的变化.
我可以通过制作此代码的N份副本来实现我的目的,并且在这里和那里进行小的改动; 但这严重违反了各种良好设计规则.理想情况下,我想编写一系列脚本,其中只包含一组config var初始化,然后是
do theCommonStuff;
Run Code Online (Sandbox Code Playgroud)
请注意,config_var(和它的兄弟姐妹)必须可用于公共代码,必须result_var和它的外观相似,在其analyzeLine()上进行一些计算.
我应该将"常用"代码打包到模块中吗?创建一个类?使用全局变量?
虽然不完全是代码高尔夫,但我正在寻找一个简单,紧凑的解决方案,这将允许我干,只为差异编写代码.我想我宁愿不从包含所有配置的巨大表中驱动代码,当然也不会使它适应使用数据库.
期待您的建议,谢谢!
更新
既然人们问,这是真实的 analyzeLine:
# Update stats with time and call data in one line.
sub processLine ($) {
my $line = shift(@_);
return unless $line =~ m/$log_match/;
# print "$1 $2\n";
my ($minute, $function) = ($1, $2);
$startMinute = $minute if not $startMinute;
$endMinute = $minute;
if ($minute eq $currentMinute) {
$minuteCount = $minuteCount + 1;
} else {
if ($minuteCount > $topMinuteCount) {
$topMinute = $currentMinute;
$topMinuteCount = $minuteCount;
printf ("%40s %s : %d\n", '', $topMinute, $topMinuteCount);
}
$totalMinutes = $totalMinutes + 1;
$totalCount = $totalCount + $minuteCount;
$currentMinute = $minute;
$minuteCount = 1;
}
}
Run Code Online (Sandbox Code Playgroud)
由于这些变量在很大程度上是相互依赖的,我认为单独计算的功能解决方案是不切实际的.我为误导人道歉.
Sin*_*nür 10
两条评论:首先,不要发布行号,因为它们比复制,粘贴和编辑更加困难.其次,不要&func()用来调用子.见perldoc perlsub:
可以使用显式
&前缀调用子例程.该&是可选的现代Perl中,...不仅在&形式使参数列表可选的,它也禁止任何原型上的论点你提供检查.
简而言之,&除非您知道自己在做什么以及为什么要这样做,否则使用可能会令人惊讶.
另外,不要在Perl中使用原型.它们与其他语言的原型不同,除非您知道自己在做什么,否则它们会产生非常惊人的效果.
不要忘记检查系统调用的返回值,例如open.使用autodie与现代perls.
对于您的特定问题,请在哈希中收集所有配置变量.将该哈希传递给analyzeLine.
#!/usr/bin/perl
use warnings; use strict;
use autodie;
my %config = (
frobnicate => 'yes',
machinate => 'no',
);
my $result;
$result += analyze_file(\%config, $_) for @ARGV;
print "Result = $result\n";
sub analyze_file {
my ($config, $file) = @_;
my $result;
open my $fh, '<', $file;
while ( my $line = <$fh> ) {
$result += analyze_line($config, $line);
}
close $fh;
return $result;
}
sub analyze_line {
my ($line) = @_;
return length $line;
}
Run Code Online (Sandbox Code Playgroud)
当然,您会注意到它$config正在遍布整个地方,这意味着您可能希望将其转换为OO解决方案:
#!/usr/bin/perl
package My::Analyzer;
use strict; use warnings;
use base 'Class::Accessor::Faster';
__PACKAGE__->follow_best_practice;
__PACKAGE__->mk_accessors( qw( analyzer frobnicate machinate ) );
sub analyze_file {
my $self = shift;
my ($file) = @_;
my $result;
open my $fh, '<', $file;
while ( my $line = <$fh> ) {
$result += $self->analyze_line($line);
}
close $fh;
return $result;
}
sub analyze_line {
my $self = shift;
my ($line) = @_;
return $self->get_analyzer->($line);
}
package main;
use warnings; use strict;
use autodie;
my $x = My::Analyzer->new;
$x->set_analyzer(sub {
my $length; $length += length $_ for @_; return $length;
});
$x->set_frobnicate('yes');
$x->set_machinate('no');
my $result;
$result += $x->analyze_file($_) for @ARGV;
print "Result = $result\n";
Run Code Online (Sandbox Code Playgroud)
继续创建一个类层次结构.您的任务是OOP编程风格的理想选择.这是一个例子:
package Common;
sub new{
my $class=shift;
my $this=bless{},$class;
$this->init();
return $this;
}
sub init{}
sub theCommonStuff(){
my $this=shift;
for(1..10){ $this->analyzeLine($_); }
}
sub analyzeLine(){
my($this,$line)=@_;
$this->{'result'}.=$line;
}
package Special1;
our @ISA=qw/Common/;
sub init{
my $this=shift;
$this->{'sep'}=','; # special param: separator
}
sub analyzeLine(){ # modified logic
my($this,$line)=@_;
$this->{'result'}.=$line.$this->{'sep'};
}
package main;
my $c = new Common;
my $s = new Special1;
$c->theCommonStuff;
$s->theCommonStuff;
print $c->{'result'}."\n";
print $s->{'result'}."\n";
Run Code Online (Sandbox Code Playgroud)