多量のメモリと速い 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