1

現在、次の行を含むテキストファイルを読んでいます。

0 24000 97200
1 52200 95400
2 0 0
3 37800 180000
4 0 0
5 48000 95400
6 0 0

最初の値は日を表し(0 = sunday, 1 = monday, ...) ます。たとえば、24000などの数値は、秒の合計量を表します。

最終的に私はこのようなものを手に入れたいと思います:

ウィークリースケジュールのグラフィック表現

これまでのコードは次のとおりです。

open(SCHEDULE, $schedule) or die print "Failed to open $schedule";
@lines = <SCHEDULE>;
@secondsfrom  = (0,0,0,0,0,0,0);
@secondsto    = (0,0,0,0,0,0,0);
@secondsextra = (0,0,0,0,0,0,0);
@days = ("sunday","monday","tuesday","wedsneday","thursday","friday","saturday");
foreach (@lines) {
    ($day, $fromtotalseconds, $tototalseconds) = split(/ /,$_,3);
    @secondsfrom[$day] += $fromtotalseconds;
    @secondsto[$day]   += $tototalseconds;
}
for (my $i=0; $i<=6;$i++) {
    print "\n@days[$i]  @secondsfrom[$i] to @secondsto[$i]";
}

この瞬間、私は立ち往生しています!私はこれらの値を次のようなものに変換する方法について何日も探していました:

日曜日24000から86400(MAX)
火曜日0から48000および52200から86400
…

これは私のためにそれが生み出すものです:

日曜日24000から97200
火曜日52200から95400
…
4

4 に答える 4

2

これがテキストによる実装です。私は実際にそれが正しいかどうかをチェックしなかったので、そこにバグが潜んでいる可能性がありますが、一度に1行ずつ入力を処理する方法を説明する必要があります。

#!/usr/bin/env perl

use strict; use warnings;

use constant ONE_MINUTE => 60;
use constant ONE_HOUR   => 60 * ONE_MINUTE;
use constant ONE_DAY    => 24 * ONE_HOUR;
use constant BLOCK_SIZE => ONE_MINUTE * 20;
use constant DAY_LENGTH => ONE_DAY/BLOCK_SIZE;

my @days = qw(Sun Mon Tue Wed Thu Fri Sat);

my $remainder = 0;

while (my $line = <DATA>) {
    next unless $line =~ m{
        \A
        ( [0-6]  ) \s+
        ( [0-9]+ ) \s+
        ( [0-9]+ ) \s+
        \z
    }x;
    my ($daynum, $start, $duration) = ($1, $2, $3);
    my $start_block = seconds_to_blocks($start);
    my $duration_block = seconds_to_blocks($duration);

    my ($dayrow, $hang) = make_dayrow(
        $remainder,
        $start_block,
        $duration_block,
    );

    printf "%3s: %s\n", $days[$daynum], $dayrow;
    $remainder = $hang;
}

sub seconds_to_blocks {
    my ($seconds) = @_;
    return int($seconds / BLOCK_SIZE);
}

sub make_dayrow {
    my ($remainder, $start, $duration) = @_;

    if ($remainder > DAY_LENGTH) {
        my $hang = $remainder - DAY_LENGTH;
        return ('#' x DAY_LENGTH, $hang);
    }

    my $hang = $start + $duration > DAY_LENGTH
             ? $duration - (DAY_LENGTH - $start)
             : 0
             ;

    my $dayrow = '#' x $remainder;
    $dayrow   .= ' ' x ($start - $remainder);
    $dayrow   .= '#' x ($duration - $hang);
    $dayrow   .= ' ' x (DAY_LENGTH - length $dayrow);

    return ($dayrow, $hang);
}


__DATA__
0 24000 97200
1 52200 95400
2 0 0
3 37800 180000
4 0 0
5 48000 95400
6 0 0

出力:

太陽: ################################################ ####
月:############################# ################### ##########
火:################################################ ##
結婚した: #########################################
木:################################################ ########################
金:##################################### ########### #####################
土:###############################################

アップデート

パーセンテージだけが必要な場合は、それも簡単です。

#!/usr/bin/env perl

use strict; use warnings;
use YAML;

use constant ONE_MINUTE => 60;
use constant ONE_HOUR   => 60 * ONE_MINUTE;
use constant ONE_DAY    => 24 * ONE_HOUR;

my @days = qw(Sun Mon Tue Wed Thu Fri Sat);

my $remainder = 0;
my @rows;

while (my $line = <DATA>) {
    next unless $line =~ m{
        \A
        ( [0-6]  ) \s+
        ( [0-9]+ ) \s+
        ( [0-9]+ ) \s+
        \z
    }x;
    my ($daynum, $start, $duration) = ($1, $2, $3);

    my $dayrow = make_dayrow($remainder, $start, $duration);

    push @rows, $dayrow->[0];
    $remainder = $dayrow->[1];
}

for my $row (@rows) {
    print join("\t", map sprintf('%.0f%%', $_ * 100), @$row), "\n";
}

sub make_dayrow {
    my ($remainder, $start, $duration) = @_;

    return [[1, 0, 0], $remainder - ONE_DAY] if $remainder > ONE_DAY;

    my $hang = $start + $duration > ONE_DAY
             ? $duration - (ONE_DAY - $start)
             : 0
             ;

    return [
        [
            $remainder / ONE_DAY,
            $start / ONE_DAY,
            ($duration - $hang) / ONE_DAY
        ],
        $hang
    ];
}


__DATA__
0 24000 97200
1 52200 95400
2 0 0
3 37800 180000
4 0 0
5 48000 95400
6 0 0

出力:

0%28%72%
40%60%40%
71%0%0%
0%44%56%
10000%
52%56%44%
66%0%0%
于 2012-05-03T11:58:32.920 に答える
0

このコードの変更はどうですか?

use strict;
use warnings;
my @lines = (
'0 24000 97200',
'1 52200 95400',
'2 0 0',
'3 37800 180000',
'4 0 0',
'5 48000 95400',
'6 0 0',
);
my @days = qw(sunday monday tuesday wedsneday thursday friday saturday);
for (my $i=0; $i<=$#lines;$i++) {
    my ($day, $fromtotalseconds, $tototalseconds) = split(/ /,$lines[$i],3);
    next if ($fromtotalseconds == 0 && $tototalseconds == 0);
    if ($tototalseconds > 86400) {
      my $remainingSeconds = $tototalseconds - (86400 - $fromtotalseconds);
      $tototalseconds = 86400;
      splice @lines, $i+1, $#lines - $i, (sprintf('%d %d %d', $day + 1, 0, $remainingSeconds), @lines[$i+1 .. $#lines]);
    }
    print "\n$days[$day] from $fromtotalseconds to $tototalseconds";
}

配列を削除し、各ループ反復の最後に印刷を配置しました。また、foreachから3引数に切り替えました。そうすれば、より多くのものを簡単に入れることができるから@linesです。$tototalsecondsto-valueが1日よりも大きいことがわかるたび@linesに、現在の行の直後に新しい行を追加します。そうすれば、次の反復で使用されます。

言わない

Tuesday from 0 to 48000 and from 52200 to 86400

しかしそれは言う

sunday from 24000 to 86400
monday from 0 to 10800
monday from 52200 to 86400
tuesday from 0 to 9000
wedsneday from 37800 to 86400
thursday from 0 to 86400
friday from 0 to 7200
friday from 48000 to 86400
saturday from 0 to 9000

私が見つけたものはそれほど悪くはありません。あなたはあなたがあなたの価値観を必要とする方法でそれを変えることができるでしょう。

ちなみに、いつもuse strictuse warnings。コードには、いくつかの警告を投げかけるものがいくつかありました。たとえば、perlの配列要素の値がスカラーの場合、:ではなく、その前にを配置する必要があり$ます。@$secondsfrom[$day] += $fromtotalseconds

編集:2番目の例。

my @lines = (
'0 9600 91200',
'1 40800 186000', 
'2 0 0', 
'3 57000 87000',
'4 63600 173400', 
'5 0 0', 
'6 0 0',);

生産:

sunday from 9600 to 86400
monday from 0 to 14400
monday from 40800 to 86400
tuesday from 0 to 86400
wedsneday from 0 to 54000
wedsneday from 57000 to 86400
thursday from 0 to 57600
thursday from 63600 to 86400
friday from 0 to 86400
saturday from 0 to 64200
于 2012-05-03T12:17:37.203 に答える
0
  open SH, "<", $schedule or die "Oh, $schedule die :\ \n";
      %hash = map{$_,0}(0..6); #not best but work, create a hash of days
      my $day;
      my $first;
      my $second;
  for(<SH>){
     ($day,$first,$second)=split(/ /,$_,3); #work? yeah
     $hash{$day}=$second-$first;
  }

  map{print "key is ".$_." value is ".$hash{$_}."\n";}sort keys %hash;
于 2012-05-03T11:18:10.383 に答える
0

これはほぼ正確にあなたが探しているものです:

use strict;
use warnings;

use constant MAX_DAY => 86_400;

my @week;
while ( <DATA> ) { 
    my ( $dow, $start, $end ) = split ' ';
    next unless $start + $end;
    while ( $end > 0 ) { 
        my $cont = $end > MAX_DAY;
        push @{ $week [ ( $dow++ ) % 7 ] }, [ $start, $cont ? MAX_DAY : $end, $cont ];
        $start  = 0;
        $end   -= MAX_DAY;
    }
}

my @dow_names = qw(Sun Mon Tue Wed Thu Fri Sat);
foreach my $day ( @week ) { 
    print shift( @dow_names ) . ': ';
    if ( ref $day ) { 
        print join( ' and '
                  , map { "from $_->[0] to $_->[1]" . ( $_->[2] and ' (MAX)' ) } @$day 
                  );
    }
    print "\n";
}
__DATA__ 
0 24000 97200
1 52200 95400
2 0 0
3 37800 180000
4 0 0
5 48000 95400
6 0 0
于 2012-05-03T12:55:48.167 に答える