ルモーリン
ホーム 更新 Perl Sample サービス 雑談 鉄ゲタ Linux リンク 連絡先

他者のPerlを書き替える

2019-12-03

解説

昔の掲示板のCGIを手直し中で、そのサンプルをご紹介します。
昔の掲示板を設置する - Perl - ルモーリン

問題

このサンプルを書き替えるとどうなりますか?

# 最終レス日順並び替え
sub dt_sort{
        foreach(@lines){
        ($dt_knum,$dt_num,$dt_date)=split(/<>/,$_);
        if($dt_num){$nxt_key=1;}else{$nxt=0;$nxt_key=0;next;} # next を $dt_num=$dt_knum に変えれば更新順
        if($nxt){next;}
        $dt_date =~ s/\///g; $dt_date =~ s/://g; $dt_date =~ s/\([^)]*\)//g;
        $no_sort{$dt_num}="$dt_date";
        if($nxt_key){$nxt=1;}
        }
        @dt_sort = sort{ "$no_sort{$b}" cmp "$no_sort{$a}"} (keys %no_sort);
        foreach(@dt_sort){
                foreach $ln(@lines){
                ($dt_knum,$dt_num)=split(/<>/,$ln);
                if(!$dt_num){$dt_num=$dt_knum;}
                if($_ == $dt_num){push(@newlines,$ln);}
                }
        }

        foreach (@lines){
        ($dt_knum,$dt_num)=split(/<>/,$_);
        if(!$dt_num){$dt_num=$dt_knum;}
                foreach $in(@dt_sort){
                if($in == $dt_num){$in_f=1;}
                }
        if(!$in_f){push(@newlines,$_);}
        $in_f=0;
        }
@lines=@newlines;
}

解答

こんな感じにしました。 このサブルーチンを呼ぶ前にログは記事/コメント毎にハッシュリファレンスにして配列に入れてログとしています。 このログは後から走査すれば、コメント→記事の順に来ますからコメントを持つ記事を簡単に把握できます。 またコメントのハッシュリファレンスを別の配列に入れて、配列のリファレンスを記事のハッシュリファレンスにぶら下げるようにして階層構造としました。 日付の比較を考えるとdateフィールド(日付文字列)の代わりにDateTimeオブジェクトを入れておけば良いなと思っています。 今は他の処理の書き直しを考えて、全部文字列のままです。

# 最終レス日順並び替え
sub dt_sort {
        my @log = @_;

        my @article_single;
        my @article_parent;
        my @comment;
        for (reverse @log) {
                if ($_->{k}) {
                        push @comment, $_;
                } elsif (@comment) {
                        my @children = reverse @comment;
                        undef @comment;
                        $_->{children} = \@children;
                        push @article_parent, $_;
                } else {
                        push @article_single, $_;
                }
        }

        @article_parent = sort {
                # 投稿日時を正規化
                my $date_a = ${$a->{children}}[0]->{date};
                $date_a =~ s/\///g;
                $date_a =~ s/://g;
                $date_a =~ s/\([^)]*\)//g;

                my $date_b = ${$b->{children}}[0]->{date};
                $date_b =~ s/\///g;
                $date_b =~ s/://g;
                $date_b =~ s/\([^)]*\)//g;

                $date_b cmp $date_a;
        } @article_parent;

        undef @log;
        for (@article_parent) {
                push @log, $_;
                push @log, @{$_->{children}};
                delete $_->{children};
        }

        push @log, reverse @article_single;
        return @log;
}