Я написал эту короткую подпрограмму для выравнивания по левому краю, центру и правому краю многострочной строки.

Как мне сделать это лучше? (короче, красивее, оптимизировано)? Вы можете предложить какие-нибудь предложения?

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

# A bit of text
$_ = <<END;
     Lorem ipsum dolor sit amet consectetur adipiscing elit
          Curabitur pretium odio dictum nisl posuere, vitae mollis nibh facilisis
   Curabitur tempus tincidunt ante eget facilisis
     Pellentesque ut accumsan dui, nec semper odio
        Ut libero lacus, fermentum quis ultricies quis, tempus nec augue
                   Mauris tincidunt hendrerit accumsan
END

# Test each alignment case
for my $align ('left', 'center', 'right') {
    print align($_, $align)."\n";
}

# Alignment subroutine
sub align {
    my ($_, $align) = @_;    # Text to align, Alignment type
    my $re = qw/^([^\n]*)$/; # A regex to match each line
    s/^[ \t]+|[ \t]+$//gm;   # Remove trailing/leading spaces

    # Get longest line
    my $max = 0;
    $max = length($1) > $max ? length($1) : $max while /$re/gm;

    # Do the alignment
    s|$re|$1 . " " x ($max-length($1))|mge if $align eq 'left';
    s|$re|" " x (($max-length($1))/2) . $1|mge if $align eq 'center';
    s|$re|" " x ($max-length($1)) . $1|mge if $align eq 'right';

    return $_;
}

Я новичок в Perl и уверен, что мне не хватает того гениального, что делает мой код волшебным.

Некоторые предложения?

0
nowox 23 Авг 2014 в 13:17

2 ответа

Лучший ответ

Я думаю, что проще всего разбить строку на строки и обработать их в цикле.

Как это

#!/usr/bin/env perl

use strict;
use warnings;
use 5.010;

sub max;

my $text = <<END_TEXT;
   Lorem ipsum dolor sit amet consectetur adipiscing elit
        Curabitur pretium odio dictum nisl posuere, vitae mollis nibh facilisis
 Curabitur tempus tincidunt ante eget facilisis
   Pellentesque ut accumsan dui, nec semper odio
      Ut libero lacus, fermentum quis ultricies quis, tempus nec augue
                 Mauris tincidunt hendrerit accumsan
END_TEXT

for my $align (qw/ left right centre /) {
  print align($text, $align), "\n";
}

sub align {

   state %core;
   @core{qw/ left centre center right /} = (0, 1, 1, 2) unless %core;

   my @lines = split m{$/}, shift;
   s/\A\s+|\s+\z//g for @lines;

   my @indices = (1, 2);
   splice @indices, $core{lc shift}, 0, 0;

   my $max = max map length, @lines;

   return join '', map {

      my $padlen = $max - length;
      my $pad1   = ' ' x $padlen;
      my $pad2   = substr($pad1, 0, $padlen/2, '');

      join '', ($_, $pad1, $pad2)[@indices], "\n";

   } @lines;
}


sub max {
  my $max = shift;
  $_ > $max and $max = $_ for @_;
  $max;
}
2
Borodin 24 Авг 2014 в 14:02

Небольшие доработки; более новый Perl выдает предупреждение при использовании $_ в качестве лексической (моей) переменной, более простой $re и правильно скомпилированный (qr против qw), удалено ненужное троичное присваивание, добавлено elsif, чтобы пропустить некоторые условия.

sub align {
    local $_ = shift;
    my ($align) = @_;    # Text to align, Alignment type
    my $re = qr/^(.*)/m;
    s/^\h+|\h+$//gm;   # Remove trailing/leading spaces

    # Get longest line
    my $max = 0;
    length($1) > $max and $max = length($1) while /$re/g;

    if    ($align eq 'left')   { s|$re|$1 . " " x ($max-length($1))|ge }
    elsif ($align eq 'center') { s|$re|" " x (($max-length($1))/2) . $1|ge }
    elsif ($align eq 'right')  { s|$re|" " x ($max-length($1)) . $1|ge }

    return $_;
}

Соблазн использовать анонимный хэш-код вместо elsif, но это не повлияет на удобочитаемость,

{
  left   => sub { s|$re|$1 . " " x ($max-length($1))|ge },
  center => sub { s|$re|" " x (($max-length($1))/2) . $1|ge },
  right  => sub { s|$re|" " x ($max-length($1)) . $1|ge },

}->{$align}->();   
4
Miller 25 Авг 2014 в 08:46