这个Perl代码有什么缺陷?

时间:2010-09-19 14:14:22

标签: arrays perl

我已经编写了一些代码来打印格式化数组(第一行=没有输入,第二行=最大数字宽度)。这颗星可以是任何一种标记,以区分其他元素。

$ cat inp.txt
6
2
1 *
2
3
4
9
12 *
$ cat inp.txt | ./formatmyarray.pl
 ____ ____ ____ ____ ____ ____ 
| *  |    |    |    |    | *  |
|  1 |  2 |  3 |  4 |  9 | 12 |
|____|____|____|____|____|____|
$
  

fomatmyarray.pl

#!/usr/bin/perl
use warnings;
use strict;

my $spc = q{ };
my $und = q{_};
my $sep = q{|};
my $end = "\n";
my @inp = <STDIN>;
my $len = $inp[0];
my $wid = $inp[1];
chomp $wid;

sub printwall {

    my($left, $middle, $l, $w) = @_;

    for(0..($l - 1)) {
        if ($_ == 0) { print $left; }
        for(1..($w + 2)) { print $middle; }
        print $left;
    }
    print $end;
 return;
}

sub printchar {

    my($left, $middle, $l, $w) = @_;

    for(0..($l - 1)) {
        if ($_ == 0) { print $left; }
        my @temp = split ' ', $inp[$_ + 2];

        my $star = 0;
        if (($#temp) >= 1) { $star = 1;    }

        my $mid = sprintf "%d", (($w + 2) /2);
        for(1..($w + 2)) {
            if (($_ == $mid) && ($star == 1)) { print "*"; }
            else { print $middle; }
        }
        print $left;
    }
    print $end;
 return;
}

sub printnum {

    my($left, $middle, $l, $w) = @_;

    for(0..($l - 1)) {
        if ($_ == 0) { print $left; }
        my @temp = split ' ', $inp[$_ + 2];

        my $format = join '', q{%}, $w, q{d};
        my $num = sprintf($format, $temp[0]);

        print join '', $middle, $num, $middle, $left;
    }
    print $end;
 return;
}

printwall($spc, $und, $len, $wid);
printchar($sep, $spc, $len, $wid);
printnum ($sep, $spc, $len, $wid);
printwall($sep, $und, $len, $wid);

我已经用Perl::Critic检查了它,但这只会告诉我语法问题(我已经纠正过了)。您是否在使用此代码时遇到任何问题。经验丰富的Perl程序员会做些什么不同的事情?

欢迎任何意见或建议。

3 个答案:

答案 0 :(得分:4)

这里有几点建议。希望这有用。

use strict;
use warnings;
use Getopt::Long qw(GetOptions);

my $SPC = q{ };
my $UND = q{_};
my $SEP = q{|};
my $END = "\n";

main();

sub main {
    # Try to keep run options and core input data separate from each other.
    GetOptions('max=i' => \my $max_n);

    # Parse input at the outset so that subsequent methods
    # don't have to worry about such low-level details.
    my $inp = parse_input();

    # Prune the input array at the outset.
    # This helps to keep subsequent methods simpler.
    splice @$inp, $max_n if $max_n;

    # Don't require the user to compute max width.
    my $wid = determine_width($inp);

    # The format string can be defined at the outset.
    my $fmt = join '', $SEP, $SPC, '%', $wid, 's', $SPC;

    # You can print both data and stars using one method.
    print_border($inp, $wid, $SPC);
    print_content($inp, $fmt, $_) for qw(star data);
    print_border($inp, $wid, $SEP);
}

sub parse_input {
    my @parsed;
    # Using <> provides more flexibility than <STDIN>.
    while (<>){
        chomp;
        my ($value, $star) = split;
        $star = $SPC unless defined $star;
        push @parsed, { data => $value, star => $star }
    }
    return \@parsed;
}

sub determine_width {
    my $inp = shift;
    my $wid = 0;
    for (@$inp){
        my $len = length $_->{data};
        $wid = $len if $len > $wid;
    }
    return $wid;
}

# Because we did work at the outset to create a data structure
# that represents our goals conveniently, generating output
# is much simpler.

sub print_border {
    my ($inp, $wid, $wall_sep) = @_;
    print $wall_sep, $UND x ($wid + 2) for @$inp;
    print $wall_sep, $END;
}

sub print_content {
    my ($inp, $fmt, $mode) = @_;
    printf $fmt, $_->{$mode} for @$inp;
    print $SEP, $END;
}

答案 1 :(得分:1)

这里有很大的改进空间(当我有时间的时候,我会更新这个答案)。

让我们从输入开始。您不必指定条目数或其最大长度,因为Perl可以为您推断:

my $entries   = my @entries = <STDIN>;

不要忘记 CPAN

例如,考虑Text::ASCIITable

答案 2 :(得分:1)

  • 返回语句不会出现在大多数人的代码中 - 一个子语件在到达结尾时返回(但请参阅注释中的讨论)。

  • 在printwall中,我无条件地在循环外打印第一个左墙;同上其他功能。

  • 我不相信我已将所有数据读入@inp,如图所示。更有可能的是,我会使用:

    my $num = <STDIN>;  # Or, more likely, just <>
    my $wid = <STDIN>;
    my @inp = <STDIN>;
    

    这将清除函数中的$inp[$_ + 2]

  • 我可能会将数组传递给函数,而不是使用全局变量 - 在Perl中,globals与其他地方一样是粗糙的。​​

  • 输入中不需要值的数量。如果数组只包含要打印的数据,您可以使用合适的foreach迭代函数中数组的每个成员,从而提高其Perlishness。

  • 在printnum中,您可以构建一次格式字符串(而不是每次迭代)。

  • 此:

    my $mid = sprintf "%d", (($w + 2) /2);
    

    是一种有趣的写作方式:

    my $mid = int(($w + 2) / 2);
    
  • 我可能会用正则表达式来寻找明星;如果找到任何字符,或者你应该打印找到的字符,是否应该打印'*'尚不清楚。

  • 我可能会使用单一格式来处理明星:

    my $fmt = sprintf "%*s%%c%*s%c", $wid, $middle, $wid, $middle, $left;
    

    我可能需要调整其中一个$ wid值以允许偶数宽度,但输出将为:

    " %c  |"
    

    然后,您可以使用格式打印每个单元格的空白或'*'。

  • 同样,在printnum中,我会生成一个简单的格式字符串,如" %2d |"来打印每个数字 - 我会生成一次该格式。