多量のメモリと速い CPU という最近のコンピュータは、 1980 年代当初の 16 bit メモリ空間ミニコン時代とは大違いで、 よほどのことがない限り、 事務処理のほとんどが簡単な awk のスクリプトで間に合いますから、
最近、そんな事務処理のスクリプトを書いていたら、 "1/23/456/7890" といった文字列に含まれる "/" の数を数えることになって、 c なら、
/* 文字列 str に含まれる文字 ch の数を数える */
chcount(str, ch) char *str;
{
int i;
for (i = 0; *str; )
if (*str++ == ch)
i++;
return i;
}
といったあたりが素直だと思いますが、
こういった発想で awk のプログラムを書くと、
例えば、こんなことになります。
# 文字列 str に含まれる文字 ch の数を数える
function chcount(str, ch, i) {
for (i = 0; length(str) > 0; str = substr(str, 2))
if (index(str, ch) == 1)
i++
return i
}
もちろん、これでも動くわけですが、ムダが多すぎます。
そこで、文字列の処理を、もう少し減らすと、
function chcount(str, ch, i, k) {
for (i = 0; length(str) > 0; i++)
if ((k = index(str, ch)) > 0)
str = substr(str, k + 1)
else
break
return i
}
となって、多少はましですが、すっきりしません。
そこで、c から awk の発想に切替えると、
function chcount(str, ch, a) {
return split(str, a, ch) - 1
}
数えなければならない文字をセパレータと考えれば、
すぐ split() が頭に浮かぶはずですが、
そうならなかったのは、c プログラミングの時代が長かったためでしょうか?
さらに、発想の転換を進めると、 gsub() によるパターンの置換を使う方法が閃いて、 最初の例なら、
str = "1/23/456/7890" print gsub(/\//, "/", str)といったトリッキーなアプローチも生まれます。
素直な発想なら split() だと思いますが、 トリッキーなアイデアはプログラミングを労働から創造に転化させ、 人生に楽しみを与えます。
任意の文字を対象にする関数なら、
function chcount(str, ch) {
return gsub("[\\" ch "]", ch, str)
}
といったところでしょうか。
gsub() のパターンに直接 ch を使わず、ch を含む文字セットにした理由は、
function chcount(str, ch) {
return gsub(ch, ch, str)
}
で "|" といった文字を数えてみるとわかると思います。
プログラミングというのは、なかなか面白い仕事で、 文字数を数えるといった簡単な仕事でさえ、 多くの楽しみを生みだします。
平林 浩一, 2007