awk の真偽を確かめる

404 Blog Not Found:js/perl/python/ruby/scheme - 真偽のほどはいかに各言語におけるtrue/falseまとめ - 床のトルストイ、ゲイとするとのことで様々な言語に対してどのような値が真でどのような値が偽になるのかをまとめられていますが、いくつかの awk のバリアントに対して調べてみます。

awk には eval() 関数がないため、以下のようなスクリプトを用いました。

#! /usr/local/bin/nawk -f
# truth.awk
# 真偽値を確かめるスクリプト
# usage: nawk -f truth.awk

# tell_me_the_truth():  真偽を確かめる
#   in:     真偽を確かめたい値 (s)
function tell_me_the_truth(s) {
    p = s;

    if (p) {
        print "'" s "' (" p ") is TRUE.";
    } else {
        print "'" s "' (" p ") is FALSE.";
    }
}

BEGIN {
    printf("空変数 True:            ");
    tell_me_the_truth(True);            # 空変数 True
    printf("文字列 False:           ");
    tell_me_the_truth("False");         # 文字列 False
    printf("数値 0:                 ");
    tell_me_the_truth(0);               # 数値 0
    printf("数値 1:                 ");
    tell_me_the_truth(1);               # 数値 1
    printf("数値 0.0                ");
    tell_me_the_truth(0.0);             # 数値 0.0
    printf("数値 nan                ");
    tell_me_the_truth("+nan" + 0);      # 数値 nan
    printf("数値 nan の比較         ");
    tell_me_the_truth("+nan" + 0 == "+nan" + 0);      # 数値 nan の比較
    printf("数値 inf                ");
    tell_me_the_truth("+inf" + 0);      # 数値 inf
    printf("数値 inf - 1            ");
    tell_me_the_truth("+inf" - 1);      # 数値 inf - 1
    printf("数値 inf - inf (= nan)  ");
    tell_me_the_truth("+inf" - "+inf"); # 数値 inf - inf (= nan)
    printf("文字列 0                ");
    tell_me_the_truth("0");             # 文字列 0
    printf("文字列 0.0              ");
    tell_me_the_truth("0.0");           # 文字列 0.0
    printf("数値 1 の否定           ");
    tell_me_the_truth(!1);              # 数値 1 の否定
    printf("数値 1 の二重否定       ");
    tell_me_the_truth(!!1);             # 数値 1 の二重否定
}

適当な eval() 関数を作成してもよかったのですが、ここではそのままです。 なお、awk の場合の Inf や NaN は "+nan" + 0 のようにします。

まずは gawk で調べます。

$ gawk -f truth.awk
空変数 True:            '' () is FALSE.
文字列 False:           'False' (False) is TRUE.
数値 0:                 '0' (0) is FALSE.
数値 1:                 '1' (1) is TRUE.
数値 0.0                '0' (0) is FALSE.
数値 nan                'nan' (nan) is TRUE.
数値 nan の比較         '0' (0) is FALSE.
数値 inf                'inf' (inf) is TRUE.
数値 inf - 1            'inf' (inf) is TRUE.
数値 inf - inf (= nan)  'nan' (nan) is TRUE.
文字列 0                '0' (0) is TRUE.
文字列 0.0              '0.0' (0.0) is TRUE.
数値 1 の否定           '0' (0) is FALSE.
数値 1 の二重否定       '1' (1) is TRUE.

想像どおりでしたか? gawk と同じ挙動を示すのが、xgawk, Busybox awk などですが、mawk や nawk は異なる挙動を示します。

$ diff -u <(gawk -f truth.awk) <(mawk -f truth.awk)
--- /proc/self/fd/112009-06-06 11:20:04.653701774 +0900
+++ /proc/self/fd/122009-06-06 11:20:04.649704485 +0900
@@ -4,7 +4,7 @@
 数値 1:                 '1' (1) is TRUE.
 数値 0.0                '0' (0) is FALSE.
 数値 nan                'nan' (nan) is TRUE.
-数値 nan の比較         '0' (0) is FALSE.
+数値 nan の比較         '1' (1) is TRUE.
 数値 inf                'inf' (inf) is TRUE.
 数値 inf - 1            'inf' (inf) is TRUE.
 数値 inf - inf (= nan)  'nan' (nan) is TRUE.

つまり、mawk は NaN の挙動が異なります。

$ diff -u <(gawk -f truth.awk) <(nawk -f truth.awk)
--- /proc/self/fd/112009-06-06 11:21:03.561955529 +0900
+++ /proc/self/fd/1220092009-06-06 11:21:03.561955529 +0900
@@ -3,11 +3,11 @@
 数値 0:                 '0' (0) is FALSE.
 数値 1:                 '1' (1) is TRUE.
 数値 0.0                '0' (0) is FALSE.
-数値 nan                'nan' (nan) is TRUE.
-数値 nan の比較         '0' (0) is FALSE.
+数値 nan                'nan' (nan) is FALSE.
+数値 nan の比較         '1' (1) is TRUE.
 数値 inf                'inf' (inf) is TRUE.
 数値 inf - 1            'inf' (inf) is TRUE.
-数値 inf - inf (= nan)  'nan' (nan) is TRUE.
+数値 inf - inf (= nan)  'nan' (nan) is FALSE.
 文字列 0                '0' (0) is TRUE.
 文字列 0.0              '0.0' (0.0) is TRUE.
 数値 1 の否定           '0' (0) is FALSE.

nawk の場合は NaN に関係するものが異なることが分かります。

各言語におけるtrue/falseまとめ - 床のトルストイ、ゲイとするとのことを見ても各言語で NaN の扱いはずいぶん異なっているようです。

NaN を真偽の判定に用いることは awk では少ないと思いますが、こうした awk のバリアントの違いによる挙動の違いも押さえておきたいところです。

tag_nawk.pngtag_nawk.pngtag_nawk.pngtag_nawk.png