我已经编写了一些代码来打印格式化数组(第一行=没有输入,第二行=最大数字宽度)。这颗星可以是任何一种标记,以区分其他元素。
$ 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程序员会做些什么不同的事情?
欢迎任何意见或建议。
答案 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 |"
来打印每个数字 - 我会生成一次该格式。
等