野狐消暇録

所感を記す

プログラミング覚え書き

  • 前書き

これから書く事は、後から読み返して、ああ、この頃はこんな事を考えていたんだ、今とあまり変わらないな、としみじみするためのものです。

  • 副作用のない関数

プログラミング言語の基礎』を読み、デザインレシピというプログラミング手順を知りました。

Amazon.co.jp: プログラミングの基礎 (Computer Science Library): 浅井 健一: 本

この手法を知った事が、僕の転機となりました。

それまで自分はプログラムを組む時、明確に手順を意識していませんでした。IOから先に書くことで、データの入口と出口を決めておき、データの変換として内部の処理を書く手法は、デザインレシピを知る前に既に知っていました。

この手法は、ちょっと思い出せないのですが、確かITコンサルタントの書いた本もしくはサイトで見たと思います。思い出してしみじみするための文章なのに、今の時点で既に思い出せない。

それはさておき、この『プログラミングの基礎』により、自分は実装上の指針を得る事になり、以後プログラムを組むのが楽になりました。

デザインレシピでは、テストを書くことをプログラム作成の工程に組み込んでいます。テスト対象のモジュールに副作用がなければ、

 

If ( succ( 1 ) == 2 )

{

 System.out.println("test 01 O.K.");

}

 

private int succ( int i )

{

 return i + 1;

}

と書ける訳です。

これで、succメソッドが正しく動いている事が確認できると思います。

そういう、副作用のない関数のテストの書き方を知ったのも、この本だったと思います。

この手法を知った事により、プログラミングにかなり自信を持てるようになりました。手順が明確に決まっているので、その通りに進めれば、プログラムが書けてしまうのです。

自分はIT業界で働きながらプログラマを目指していたので、相当良かった訳です。

プログラミングの勉強をする中で、分割統治という手法を知りました。

これはアルゴリズムを学ぶと出てくるのですが、問題を小さな問題に分割し、小さな問題を解決していく事で、元の問題を解決するというやり方を指すようです。

プログラムにバグがある場合、これに似た手法で解決する事でできる事があります。

例えば、あるプログラムが実行途中で異常終了するとします。

その場合、自分は以下の方法を取ります。

 1. 問題発生個所の特定

まず、異常終了が発生しそうにない、当然実行されていると思われるソースコード上の箇所に以下を書きます。

System.out.println("実行されていると僕は信じる。");

このデバッグ用メッセージが出力されている事が確認できれば、この箇所までは正常に動作しているであろうことが分かります。

さらに、異常終了のすぐそばと思われる所に似たデバッグコードを仕込みます。

System.out.println("実行されていると俺は信じるぜ。");

この時、最初に書いたコードと同じメッセージを出力してはいけません。

どっちが実行されたのか、区別できない事があるためです。

そうやって、デバッグメッセージを沢山出力していくと、ある所で、メッセージが出力されなくなります。その場合、その出力されないメッセージを仕込んだソースコード上の位置より前の時点で、プログラムが異常終了している事になります。

プログラムにメッセージ出力させることでデバッグする方法はprintfデバッグと呼ばれるようです。これはおそらく、C言語でメッセージを出力する時、「printf」という命令文を使う所から来ているのだろうと思います。

自分はJavaで書く事が多いので、さながら「System.out.println debug」、つまり「システム・アウト・プリントライン・デバッグ」といった所でしょうか。とはいえ、「システム・アウト・プリントライン・デバッグ」という言葉を実務で使った事はなく、個人の開発で使った事もありません。たった今、ブログを書いていて思い付いただけです。おそらく人が

「プリントエフ・デバッグ

と言ったとき、

「プリントエフ・デバッグ? ああ、システム・アウト・プリントライン・デバッグのことね?」

と言ったら、とてもうざったい人だと思われるでしょう。

それはそれとして、なぜprintfデバッグが分割統治法と関係しているかと言えば、この方法を使う時、単に最初の方にメッセージ出力を仕込んで、徐々に異常発生個所に近付けるのではなく、大雑把にメッセージ出力を仕込み、出力されたり、されなかったりを繰り返しながら、異常の発生個所を絞り込んでいく事が多いからです。

これは、アルゴリズムでいう所の二分探索と同じ方法です。

プログラムが逐次実行される場合は、この方法が使えると思います。

今調べていて気がついたのですが、二分探索は分割こそしているかもしれませんが、別に結果をマージしたりしないので、統治はしていないかもしれません。つまり、分割統治法ではないのではないかと思い始めました。

しかし、バグを発見する事で、ローマ帝国が都市を治めたように、「話を収める」という点で、統治していると考えれば、大丈夫かもしれない。問題の解決が統治であるという訳です。

この分割統治、乃至二分探索的デバッグ手法は、プログラムに限らず、他の分野での問題調査にも使えます。

最近経験した所だと、ネットワークの障害で、相手先の機器に信号が届かない事がありました。

自分は次のようにしました。

① 現状の把握

HTTP通信が正常に動いている事は分かっていました。HTTPはTCPで動いているので、これでTCP通信が正常に動作している事も保証されます。

② 問題の調査

その通信はUDPを使っていました。UDP通信がうまくいっていないかもしれません。UDPの通信はうまくいっていて、通信先のプログラムが異常終了しているかもしれません。

ここは二分探索です。ざっくりと問題を切り分けるのです。

まずはUDP通信が動作している事を確認しました。

使用したツールは以下です。

Portqry.exe コマンド ライン ユーティリティの説明

このツールを使うと、UDP通信が出来ている事を確認できます。

通信は正常にできていました。相手先のアプリケーションにも届いているようです。

となると、ネットワークはO.K.です。アプリケーションの問題である可能性が大きいです。

 ② 推測

ここで思い当たるのは、このUDPはブロードキャストを使っている事です。

ブロードキャストに失敗しているかもしれません。

まずはユニキャストでパケットを送ってみて、うまく動いたら、いよいよブロードキャストが怪しい。

もしブロードキャストに失敗していて、ユニキャストで成功するなら、ユニキャストで相手先の機器にひとつひとつパケットを投げよう、と思いました。

ちょっとダサいですが、背に腹は代えられません。

② 更なる調査

実際の調査は自分はしていないのですが、調査用のプログラムを作成し、調査を依頼した所、ブロードキャストで失敗している事が分かりました。

③ 解決

自分も初めて知ったのですが、UDPのブロードキャストには、制限ブロードキャスト(255.255.255.255)とローカル・ブロードキャスト(192.168.X.255)があり、それぞれセグメント単位、ローカル・ネットワーク単位でブロードキャストするそうです。今回は制限ブロードキャストを使っていたので、同じローカル・ネットワーク上の相手に通信が届かなかったようです。ローカル・ブロードキャストに送り元プログラムを変更し、無事解決しました。

これも、現状を把握して失敗している可能性がある箇所を絞り込み、大まかにネットワークの調査から始めて、アプリケーションの問題だと分かり、障害箇所に辿り着いたわけです。

この方法は、おそらく機械の保守作業でも使えるのではないかと思っています。

 

 2. 問題の解決

そうやって問題の発生個所が特定できたら、あとはその問題を解決します。

そのために必要な事は以下です。

「じっと見よ、されば間違いに気付かん」