2 つのテキスト内の数字を 1 行ずつ割り算する

2009-06-09 - 苦悩するだめ日記5に書かれてある問題を解いてみます。 解くための方法はいくつかあると思いますが、paste コマンドを使うと一行野郎で記述することができます。

$ cat total.txt
25
20
0
14

$ cat ok.txt
20
18
0
0

$ paste ok.txt total.txt | nawk '$0=$2?$1*100/$2"\nGYO = "NR:"GYO = "NR'
80
GYO = 1
90
GYO = 2
GYO = 3
0
GYO = 4

awk は paste コマンドとは非常に相性が良いのですが、逆に paste コマンドを使うと簡単なものを awk 単独で行おうとすると難しくなります。

#! /usr/local/bin/nawk -f
# total_ok.awk
# 2 つのテキスト内の数字を 1 行ずつ割り算する
# usage: nawk -f total_ok.awk file1 file2

BEGIN {
    total_file = ARGV[1];
    ok_file    = ARGV[2];

    while (getline < total_file > 0) {
        total[++nr1] = $0;
    }
    close(total_file);

    while (getline < ok_file > 0) {
        ok[++nr2] = $0;
    }
    close(ok_file);

    for (i = 1; i <= nr2; i++) {
        if (total[i] == 0) {
            print "GYO = " i;
        } else {
            print ok[i] * 100 / total[i];
            print "GYO = " i;
        }
    }
}

実行してみると以下のような結果が得られます。

$ nawk -f total_ok.awk total.txt ok.txt
80
GYO = 1
90
GYO = 2
GYO = 3
0
GYO = 4

これでは awk らしくなく、全てを配列に格納するため、メモリ効率も悪いので、以下のようなものを作りました。

#! /usr/local/bin/nawk -f
# total_ok_2.awk
# 2 つのテキスト内の数字を 1 行ずつ割り算する
# usage: nawk -f total_ok_2.awk file1 file2

BEGIN {
    ok_file    = ARGV[1];
    total_file = ARGV[2];
}

FILENAME == ok_file {
    ok[FNR] = $0;
}

FILENAME == total_file {
    if ($0 == 0) {
        print "GYO = " FNR;
    } else {
        print ok[FNR] * 100 / $0;
        print "GYO = " FNR;
    }
}

前述のものとファイルの指定順序が異なりますが、以下のように実行します。

$ gawk -f total_ok_2.awk ok.txt total.txt
80
GYO = 1
90
GYO = 2
GYO = 3
0
GYO = 4

他にも様々な方法があると思いますので、いろいろ試してみてください。

tag_nawk.pngtag_nawk.pngtag_nawk.pngtag_nawk.png