[SPOILER] Perl 'Easy' Quiz of the Week #2005-2

Roger Burton West <roger-UvLOT2mcgw/[email protected]> Sun, 13 Feb 2005 11:03:38 +0000
Newsgroups gmane.comp.lang.perl.qotw.discuss
Message-ID <[email protected]>
On Wed, Feb 09, 2005 at 09:30:52AM -0800, Dan Sanderson wrote:

>Write a subroutine called print_list that takes a number indicating
>the width of the display as a number of characters, and a list of
>strings, and prints the strings in a sorted columnar display, using as
>many columns that will fit in the display for the given list and as few
>rows as possible.

Intriguingly complex once one looks at it...

sub print_list {
  my ($width,@list)=@_;
  my $spacing=2;
  @list=sort @list;
  my @len=map {length($_)} @list;
  $width=&max($width,@len);
  push @len,(0) x scalar @len;
  my $columns=scalar @list;
  my $startinc;
  my @colsize;
  while (1) {
    $startinc=int(scalar @list/$columns+.999999);
    @colsize=();
    foreach my $cn (0..$columns-1) {
      $colsize[$cn]=&max(@len[$startinc*$cn..$startinc*($cn+1)-1])
    }
    if (&sum(@colsize[0..$columns-1],$spacing*($columns-1)) <= $width) {
      last;
    } else {
      $columns--;
    }
  }
  my $format=join(' ' x $spacing,map {"%-${_}s"} @colsize[0..$columns-1])."\n";
  foreach my $row (0..$startinc-1) {
    my @c;
    foreach my $cn (0..$columns-1) {
      my $i=$startinc*$cn+$row;
      if ($i<=$#list) {
        push @c,$list[$i];
      }
    }
    printf($format,@c);
  }
}

sub max {
  my @t=@_;
  my $a=$t[0];
  foreach my $b (@t[1..$#t]) {
    if ($b>$a) {
      $a=$b;
    }
  }
  return $a;
}

sub sum {
  my @t=@_;
  my $a=$t[0];
  foreach my $b (@t[1..$#t]) {
    $a+=$b;
  }
  return $a;
}

Sorry, no PostScript version...

R