Excelでmandelbrot集合の描画をする実験が思いの外うまく行ったので気を良くして普通の直線を描画するアルゴリズムを実装しようとしていたんだがここでハマってしまったのでメモ
画素を指定して点単位で色を着けられる画面(つまり今回のExcelのセル幅と高さを1ピクセルに調整してセルの背景色で色を着けられるような場合)マンデルブロのように各点ごとに条件を計算して色をつければ完成するようなものよりも直線の描画のように始点と終点をしていして(必要なら色の指定もして)描画するような操作や中心と半径を指定して円を描画するようなプログラム部品が欲しくなる。
そして直線をなるべく少ない計算量で描画するのがブレゼンハムのアルゴリズムである。
ここで事件は起きた。
まずは手持ちの資料がCだったのでそれを逐語訳的にVBAに置き換えて動かして見た。直接デバッグウィンドウに
drawline 0,0,639,479,vbRed
のように打ち込んで実行するときれいに直線が描画される。しかし
for x=0 to 639:drawLine x,0,639-x,479,vbBlue:next
等とするととたんにおかしくなる。xの区間がある程度までは問題なく描画されるのだがx=80ぐらいから妙なことに画面の中央ぐらいでチラチラするだけでうまく行かない。
単一行だとうまくいくのに連続の実行だと変になる。最初はわけが分からなかった。
ソースをながめること小一時間。あれこれ、パラメータを受けてそのまま作業変数として使ってるぞ。そうか参照渡し(もしくは変数渡し)と値渡しの違いなのか。
Cとかpascalとかは値渡しが基本です。渡された関数(pascalだとprocedureもありね)はパラメータを内部処理でローカル変数と代入し合ったりしても渡したもとのプログラム側では値が変わってしまうことは値渡しということで無い。しかしvbaは基本的には参照渡し。
つまり上記のforの例のように繰り返し管理に使うxの値が途中で変わってしまって上手く行かなくなったようだ。
ここまでわかれば対策はかんたん。関数(手続き)側のパラメータ宣言部の変数に前にbyvalを付ければ目的は果たせるのだけど今回は呼び出し箇所が1箇所だったので
for x=0 to 639:drawLine (x),0,(639-x),479,vbBlue:next
みたいに呼び出し側で式化して値を渡すようにして逃げた。
仕事で各プログラムだと繰り返しの変数を直接パラメータに渡すような処理を書くようなことがまず無いせいですっかり忘れてたが副プログラムの処理で変数の内容が変わるような副作用を起こせたんだ。やはり基本的な(そして大事な)変数まわりの処理や副作用には常に気を配らねばと思った。
日が変わってやっぱり呼び出し側に気を使わせるよりは手続き側のパラメータ指定にbyValをつけるほうが呼び出しのたびにコピーが生じるとしても正しいと考えて手続きの冒頭を
sub drawline(byVal x1 as integer,・・・・,col)
のように書き直した。
将来の自分が呼び出し側で今回のような配慮や工夫が必要って覚えている保証無いもんね。
0 件のコメント:
コメントを投稿