Можно ли эмулировать это поведение Perl с помощью переключателя / регистра или заданного / когда? - PullRequest
1 голос
/ 21 мая 2009

Мне было интересно, есть ли у кого-нибудь предложения по улучшению следующего кода (если это возможно), чтобы он не нуждался в повторении (my @a = $ time = ~ ...), возможно, с использованием case / switch или учитывая / когда или какая-то другая идея, что я скучаю?

my $time = '12:59pm';

if( my @a = $time =~ m/^(\d\d?)(am|pm)$/ )        { tell_time( $a[0], 0, $a[1] ) }
if( my @a = $time =~ m/^(\d\d?):(\d\d)(am|pm)$/ ) { tell_time( @a ) }
if( my @a = $time =~ m/^(\d\d?):(\d\d)$/ )        { tell_time( @a ) }

sub tell_time
{
    my $hour    = shift;
    my $minute  = shift || '00';
    my $ampm    = shift || ( $hour > 12 ) ? 'pm' : 'am';

    print "Hour: $hour, Minute: $minute, AMPM: $ampm\n";
}

Я пытался поиграть с Switch и 5.10, заданным / когда, но, похоже, не смог сделать что-то вроде:

given( $time )
{
    when( /^(\d\d?)(am|pm)$/ )        { tell_time( $_[0], 0, $_[1] ) }
    when( /^(\d\d?):(\d\d)(am|pm)$/ ) { tell_time( @_ ) }
    when( /^(\d\d?):(\d\d)$/ )        { tell_time( @_ ) }
}

Это не летит, потому что @_ хранит $ time.

также обратите внимание Меня больше интересует синтаксис проблемы, чем проблема, которую решает код. Я хорошо знаю, что мог бы использовать Time :: ParseDate, чтобы выяснить различные части строки, отформатированные как время или дата.

Ответы [ 6 ]

10 голосов
/ 21 мая 2009

Ваше регулярное выражение использует () для извлечения совпадений, но вам не нужно хранить их в массиве. Если хотите, они хранятся в $1, $2, $3 и т. Д. Lookie:

given( $time )
{
    when( /^(\d\d?)(am|pm)$/ )        { tell_time( $1, 0, $2 ) }
    when( /^(\d\d?):(\d\d)(am|pm)$/ ) { tell_time( $1, $2, $3 ) }
    when( /^(\d\d?):(\d\d)$/ )        { tell_time( $1, $2 ) }
}

Делает именно то, что, я думаю, вы хотите сделать.

Если вы хотите добавить синтаксис, я бы написал tell_time(), чтобы просто взять время в виде строки и заставить функцию анализировать сам результат, а не заставлять пользователя вашего кода анализировать его сам. В качестве альтернативы, вы можете использовать этот блок given() в качестве начала новой функции, которая выполняет именно это - анализирует строку времени и правильно передает ее в tell_time(). Но это только я. Я не знаю, для чего вам нужен ваш код, поэтому обязательно сделайте это.

1 голос
/ 21 мая 2009

Поскольку вы используете 5.10, вы также можете использовать именованные захваты в своем регулярном выражении:

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

my $hour24   = qr/(?<hour>[1-9]|1[0-9]|2[0-3])/;
my $hour12   = qr/(?<hour>[1-9]|1[0-2])/;
my $minute   = qr/(?<minute>[0-5][0-9])/;
my $meridiem = qr/(?<meridiem>am|AM|pm|PM)/;

for my $time (qw(5pm 10am 5:59pm 10:00pm 5:00 22:00 24:00)) {
    given($time) {
        when(/ ^ $hour12 $meridiem $ /x) { 
            my $hour = $+{hour};
            $hour += 12 if 'pm' eq lc $+{meridiem};
            tell_time($hour, "00") 
        }
        when(/ ^ $hour12 : $minute $meridiem $ /x) { 
            my $hour = $+{hour};
            $hour += 12 if 'pm' eq lc $+{meridiem};
            tell_time($hour, $+{minute}) 
        }
        when(/ ^ $hour24 : $minute $ /x) { 
            tell_time($+{hour}, $+{minute}) 
        }
        default {
            say "bad time: $time";
        }
    }
}

sub tell_time {
    my ($hour, $minute) = @_;
    say "it is $hour:$minute";
}
1 голос
/ 21 мая 2009

Ну, без использования switch / case, я бы просто использовал одно регулярное выражение для захвата всех вариантов ...

#!/usr/bin/perl

tell_time ("12:59am");    # matches time format 1
tell_time ("2:59pm");     # matches time format 1
tell_time ("12am");       # matches time format 2
tell_time ("12:59");      # matches time format 3
tell_time ("14:59");      # matches time format 3
tell_time ("12:59:59am"); # produces no output, does not match any known time formats.

sub tell_time
{
    my $timearg = shift;

    # note: (?: ... ) creates a non-capturing group, which is not reflected in 
    # the returned array.
    my ($hour , $minute, $ampm) = ( $timearg =~ m/^(\d\d?)(?::(\d\d?))?(am|pm)?$/ ) ;

    # only continue if we captured all required fields (i.e. hour)
    if($hour)
    {
        # set default values for optional fields (i.e. minute, ampm) if necessary
        $minute ||=  '00';
        $ampm ||=  ( $hour > 12 ) ? 'pm' : 'am';

        print "Hour: $hour, Minute: $minute, AMPM: $ampm\n";
    }

}

Я могу объяснить это дополнительно, если это необходимо, но я думаю, что если вы можете читать Perl, должно быть ясно, что он делает ...

0 голосов
/ 21 мая 2009

Я создаю переключатель с меткой блока, например:

my $time = '12:59pm';
SWITCH: {
    $time =~ /^(\d\d?)(am|pm)$/ && do { 
        tell_time($1,0,$2);
        last SWITCH;
    };
    $time =~ /^(\d\d?):(\d\d)(am|pm)$/ && do {
        tell_time($1,$2,$3);
        last SWITCH;
    };
    $time =~ /^(\d\d?):(\d\d)$/ && do {
        tell_time($1,$2);
    };
}
0 голосов
/ 21 мая 2009

Я не уверен, важен ли данный / когда аспект здесь. Я бы просто объединил возможные шаблоны в одном регулярном выражении. В сочетании со специальной переменной% + и определенным оператором или мы можем сделать код более лаконичным.

#!/usr/bin/perl

use strict;
use warnings;

my @times = qw( 12:59pm 12 1pm 13:11 11 11pm);

my $hour_pat   = '(?<hour>[0-9]{1,2})';
my $minute_pat = '(?<minute>[0-9]{2})';
my $ampm_pat   = '(?<ampm>am|pm)';

my $re = qr{
    \A
    (?:$hour_pat : $minute_pat $ampm_pat)
    |
    (?:$hour_pat : $minute_pat)
    |
    (?:$hour_pat $ampm_pat)
    |
    (?:$hour_pat)
    \z
}x;

for my $time ( @times ) {
    if ( $time =~ $re ) {
        tell_time( %+ );
    }
}

sub tell_time {
    my %time = @_;
    printf( "Hour: %2.2d, Minute: %2.2d, AMPM: %s\n",
        $time{hour},
        $time{minute} // 0,
        $time{ampm} // ( $time{hour} >= 12 ? 'pm' : 'am' ),
    );
    return;
}
0 голосов
/ 21 мая 2009

Крис Латс уже описал синтаксис переключателя, используя Perl 5.10. В заказных версиях Perl вы можете использовать псевдоним цикла для эмуляции одного:

for ($time) {
  /^(\d\d?)(am|pm)$/        && do { tell_time( $1, 0, $2 );  last };
  /^(\d\d?):(\d\d)(am|pm)$/ && do { tell_time( $1, $2, $3 ); last };
  /^(\d\d?):(\d\d)$/        && do { tell_time( $1, $2 );     last };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...