再帰呼出し型サブルーチン

ソースをコンパクトにまとめる一つの方法として、たぶん少なからず使われるのが「再帰呼出し」が可能なサブルーチンでしょう。僕は恐くてあまり使えませんが・・・。

なぜ恐いかというと、無限ループに陥りやすいからです。

「こんなもんだろう」という感じで「とりあえず実行」をしてしまうので、この手のサブルーチンは苦手なのです。そのおかげか知りませんが、強制終了は上手くなりました(自爆)

数少ない成果として、「数値の整形スクリプト」を作ってみました。これだとループさせてもよさそうなものですが、なんとなく。でも、結構シンプルにまとまっているでしょう?(自画自讃)

もともと「TestCGI Index」のバイト数表示用に作ってみたのですが、結構緊張しました。こちらは「1MB」を越えることは無い(と思う)ので、本当は再帰は必要ないのですけどね・・・。

ただ、この手の機能はよく使われると思うので、ライブラリがあると思うのですけど、全然把握してません。必要な機能を自分で作るのも勉強です(苦笑)

また、ディレクトリの検索にも「再帰呼出し」を使っていますが、この場合、「..(親ディレクトリ)」の処理を忘れると大変です。とほほさんの検索スクリプトを見ていなかったら、迷わず嵌っていた事でしょう(笑)

ちなみに、明らかに数値しか扱わない場合は正規表現を使えば簡単に整形できます。

1
2
3
4
5
sub divideNum {
    my ($num) = @_;
    1 while $num =~ s/(d+)(d{3})/$1,$2/;
    return $num;
}

変換部分は、とほほさんのラウンジで見たのを多少アレンジしたものですが、最初見たときには目から鱗が落ちました。正規表現を極めると面白そうです。

そんなわけで、上の「(自画自讃)」はかなりボケてます(爆)

ここから追記(2000/12/14)

「再帰呼出し」とは関係ないですが、数値の整形については「Perlメモ」の「数字を 3桁ごとにコンマで区切る」が、かなり詳しいです。

ソースコード

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/perl

#BEGIN{
#   print "Content-type: text/plainnn";
#   open(STDERR, ">&STDOUT");
#   $|=1;
#}

$usr_title = 'テスト29';

require "tsenv.pl";

{
    $rh_form = getForm();

    if(exists $rh_form->{num}){
        if($rh_form->{num}){
            outputNumber($rh_form->{num});
        }else{
            error('Data is `Nothing` or `Zero`');
        }
    }else{
        inputNumber();
    }

    exit(0);
}

sub error{
    my($err, $num) = @_;
    print "Content-type: text/html; charset=iso-8859-1nn";
    print "<html lang=en><head><title>Error</title></head>n";
    print "<h1>$err</h1>n";
    print "<p>Request is `$num`</p>n" if $num;
    print "</body></html>n";
    exit(1);
}

sub inputNumber{
    my $title = $usr_title.'(数値入力)';
    printHeader($title);
    printBodyHeader($title);

    print qq(<form action="$ENV{SCRIPT_NAME}">n);
    Jprint('<p>不要っぽい「0」を消し、3桁ずつ区切って表示します。'."n");
    Jprint('<p>適当な数値を入力してください。'."n");
    print qq(<p><input type=text name=num size=30>n);
    Jprint(qq(<p><input type=submit value="区切ってみる">n));
    Jprint(qq(<input type=reset value="とりあえず消す">n));
    print qq(</p></form>n);

    printFooter();
}

sub outputNumber{
    my($num) = @_;
    my $outnum = numbering($num);

    my $title = $usr_title.'(結果出力)';
    printHeader($title);
    printBodyHeader($title);

    print "<table border=1 cellpadding=4>n";
    Jprint(qq(<tr><th>入力された値<td>$numn));
    Jprint(qq(<tr><th>整形後<td>$outnumn));
    print "</table>";
    Jprint('<p>合ってるでしょうか・・・?');

    printFooter();
}

sub getForm{
    my $method = $ENV{REQUEST_METHOD};
    my $len = $ENV{CONTENT_LENGTH};
    my($query, $key, $value, @elm, %form);
    if($method eq 'POST'){
        error('Memory Over') if(8191 < $len);
        read(STDIN, $query, $len);
    }elsif($method eq 'GET'){
        $query = $ENV{QUERY_STRING};
    }else{
        error('Unknown Request');
    }
    @elm = split('&amp;', $query);
    foreach(@elm){
        ($key, $value) = split('=');
        $form{$key} = $value;
    }
    return %form;
}

sub numbering{
    my($num) = @_;
    my($f);
    error('Not a Number', $num) if($num !~ /^[0-9]*.?[0-9]*$/);
    $num =~ s/^0+//;
    if($num =~ /./){
        ($num, $f) = split(/./, $num);
        $f =~ s/0+$// if $f;
    }
    $num = '0' unless $num;
    $num = divideNum($num);
    $num .= '.'.$f if $f;
    return $num;
}

sub divideNum{
    my($num) = @_;
    my $len = length($num);
    if(3 < $len){
        my $hn = substr($num, -3);
        my $th = divideNum(substr($num, 0, $len - 3));
        return $th.','.$hn;
    }else{
        return $num;
    }
}

Comments

comments powered by Disqus