我想将数据库表打印到STDOUT。如果表格宽度大于屏幕宽度,我希望以相同的百分比(除非表格宽度达到min_width
)切割列,直到表格适合屏幕。我试图通过发布的子程序来解决这个问题。有人知道一个更短更优雅的算法来解决这个问题吗?
sub cal_size {
my ( $maxcols, $ref ) = @_;
# $maxcols => screen width
# $ref => ref to an AoA; holds the table
my ( $max ) = cal_tab( $ref );
# $max => ref to an array; holds the length of the longest string of each column
# $tab = 2;
if ( $max and @$max ) {
my $sum = sum( @$max ) + $tab * @$max;
$sum -= $tab;
my @max_tmp = @$max;
my $percent = 0;
while ( $sum > $maxcols ) {
$percent += 0.5;
if ( $percent > 99 ) {
return;
}
my $count = 0;
for my $i ( 0 .. $#max_tmp ) {
# $min_width => columns width should not be less than $min_width if possible
next if $min_width >= $max_tmp[$i];
# no need to cut if the column width id less than min_width
next if $min_width >= minus_x_percent( $max_tmp[$i], $percent );
# don't cut if column width become less than min_width
$max_tmp[$i] = minus_x_percent( $max_tmp[$i], $percent );
$count++;
last if $sum <= $maxcols;
}
$min_width-- if $count == 0 and $min_width > 1;
# if no cuts but $sum > $maxcols reduce $min_width
$sum = sum( @max_tmp ) + $tab * @max_tmp;
$sum -= $tab;
}
my $rest = $maxcols - $sum;
while ( $rest > 0 ) { # distribute the rest
my $count = 0;
for my $i ( 0 .. $#max_tmp ) {
if ( $max_tmp[$i] < $max->[$i] ) {
$max_tmp[$i]++;
$rest--;
$count++;
last if $rest < 1;
}
}
last if $count == 0;
last if $rest < 1;
}
$max = [ @max_tmp ] if @max_tmp;
}
return $max;
}
sub minus_x_percent {
my ( $value, $percent ) = @_;
return int $value - ( $value * 1/100 * $percent );
}
答案 0 :(得分:2)
如果不是字段宽度的下限,则此问题很简单。一旦某个字段无法变小,只有较大的字段才有资格进行缩放,因此计算方式会有所不同,具体取决于是否所有字段,非字段或某些字段都已缩小到其限制。
缩放具有多个波段,每个唯一的场宽度一个。由于字段按比例缩小,其中最小的字段将是第一个达到最小字段大小限制的字段。之后,只有大于最小尺寸的列才能进一步减小,直到第二个最小值也达到极限。
这一直持续到所有列都达到最小值,之后可用空间在列之间平均分配。
这个程序实现了该算法的计算,我想你做了什么。
请注意,返回的字段宽度是浮点值,您必须根据需要对它们进行舍入。
use strict;
use warnings;
use List::Util 'max';
my $min_col_width = 10;
my $tab = 2;
my $widths = recalc_widths(80, [ 10, 15, 20, 25, 30 ]);
print join ' ', map sprintf('%.3f', $_), @$widths;
print "\n";
sub recalc_widths {
my ($target, $widths) = @_;
$target -= (@$widths - 1) * $tab;
my @sorted_widths = sort { $a <=> $b } @$widths;
my $num_limited = 0;
my $adjustable_total_width = 0;
$adjustable_total_width += $_ for @sorted_widths;
while (@sorted_widths) {
my $boundary = $sorted_widths[0];
my $scale = ($target - $num_limited * $min_col_width) / $adjustable_total_width;
if ($boundary * $scale >= $min_col_width) {
return [ map max($_ * $scale, $min_col_width), @$widths ];
}
while (@sorted_widths and $sorted_widths[0] == $boundary) {
shift @sorted_widths;
$adjustable_total_width -= $boundary;
$num_limited++;
}
}
return [ ($target / $num_limited) x $num_limited ];
}
<强>输出强>
10.000 10.333 13.778 17.222 20.667
答案 1 :(得分:-1)
我认为最好使用CPAN中的适当模块。如果可以计算每列的宽度,则使用Text::Format
配置每行打印