2009-05-21 5 views
1

誰かが(可能であれば)次のコードを改善するための提案があったのだろうかと思っていたのですが、それは繰り返し(my @a = $ time =〜...)、case/switchまたはgiven /いつ私が行方不明になっているか他のアイデア?このPerlの振る舞いは、スイッチ/ケース、または指定された/ whenでエミュレートできますか?

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"; 
} 

私はスイッチで遊んで試してみたと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/ParseDateを使用して、時間や日付のような書式設定された文字列のさまざまな部分を把握できることをよく知っています。

答えて

10

あなたの正規表現では()を使用して一致を抽出しますが、これを配列に保存する必要はありません。必要に応じて、$1$2$3などに格納されます。ルック:

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()と表示されます。あるいは、この​​ブロックを、時間ストリングを正確に解析してそれを正しくtell_time()に渡す新しい機能の開始点として使用できます。しかし、それは私だけです。私はあなたのコードが何をする必要があるのか​​わからないので、是非ともそれを求めてください。

+0

感謝。私はまず、Switch.pmで正確にそれを試してみましたが、うまく動作しませんでした。 関数をさらに改善するという点では、実際の例は実際にはPragmatic Programmerのコードサンプルを書き直したものであり、そのコンセプトは私には興味がありましたが、特にコード実装が好きではありませんでした。 ご協力いただきありがとうございます。 – stevecomrie

1

まあ、スイッチ/ケースを使用せずに、私はすべてのバリエーションをキャプチャするために、単一の正規表現を使用したい...

#!/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

私は最後のものが失敗すると思われますか?それは、「23行目の数値で、$ hourという初期化されていない値を使用する必要がある」と思われる場所では失敗しません。 –

+0

@Chris Lutz:最後のものは、出力が出ないと思われていました。私はそのテストイヤラーを動かすべきです...しかし、それは私のために失敗しないという面白いことです。 /私はそれを確認するために行く。 – Stobor

+0

私の印象は、元のポスターがスイッチのような構文に興味があるということです。 1つの正規表現を使用すると、このエクササイズの目的が無効になります。 –

0

Chris Lutzは既にPerl 5.10を使ってスイッチ構文をカバーしています。 Perlののためのバージョンでは、1をエミュレートするためにループエイリアシングを使用することができます。

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 }; 
} 
+0

ソースフィルタを使用してswitch文をエミュレートするスイッチモジュールはありませんか?これは多かれ少なかれ舞台裏でやっていることなのでしょうか? –

+0

あります。私はそれを使用したことがない、またはそれを見たことがありますが、これはどのように動作するのかを推測します。 –

+0

これは私が探していたラインに沿っています。 5.10よりも少し冗長であるが、それでもなお満足できる。そして、Switch.pmについては、私がChrisの答えに対する私のコメントで述べたように、switch/caseステートメントで$ 1、$ 2、$ 3などを使ってみましたが、すべてがundefに戻りました。 – stevecomrie

1

あなたは5.10を使用しているので、あなたが同様にあなたの正規表現でnamed capturesを使用することがあります:

#!/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"; 
} 
+0

私は実際に軍事的な時間も好むが、私はOPがAM/PMにディスプレイを保つことを好むだろうと思う。しかし、私は興味があります - 名前のついたキャプチャよりも効率が悪いキャプチャという名前はありますか?名前のついていないものと比較してパフォーマンスヒットの(小さな)ビットがあるか、バイトコードのすべてが同じであるようです。 –

+0

@Chris Lutz tell_timeの実装は指定されていませんでしたので、テスト目的のために簡単な実装を提供しました。パフォーマンスのヒットがあるかどうかはわかりませんが、ベンチマークの時間です。 –

+0

@Chris Lutz私のベンチマークでは、通常のキャプチャは20%高速です。 –

0

私がもしわかりませんここではアスペクトが重要なときに与えられます。私は単一の正規表現で可能なパターンを組み合わせるだけです。特殊変数%+とdefined-or演算子を組み合わせることで、コードをより簡潔にすることができます。

#!/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

私はこのように、ブロックのラベルでスイッチを作成します:ヘッドシェイクのため

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); 
    }; 
} 
関連する問題