『共用体』からパクってみる

 ここで言う『共用体』とは、C言語などで一つのメモリ領域に複数の変数名や変数型を付けることができるプログラミング・テクニックあるいは規則のことを言います。「ある時は片目の運転手、またある時は流しの歌い手、そしてまた、ある時は、しがない私立探偵…しかしてその実体は…」という七つの顔を持つ男、多羅尾伴内みたいな話ですが、実際には、特殊なデータ操作以外に余り有効利用されていないとも、プログラマの間では言われてきました。よくわからない人は、ネットで検索して調べてみてください。
 今回私は、以前『人工知能もどき開発事情』(2018年11月19日)で述べたHTMLアプリケーションのプログラム(Reversi V1.55.hta)を作るに際して、この『共用体』の考えを参考にしました。そうすることによって、VBスクリプトという簡易言語で簡単にプログラミングする方法の一つを見つけてしまいました。
 VBスクリプトと言うと、BASIC言語をルーツとする初心者用プログラミング言語をイメージする人も多いと思います。そして、いつまでもそのような簡易言語を使い続けていると、プログラミングが困難になるほど馬鹿になってしまう(つまり、コンピュータ・プログラミングが上達しない)と、教え込まれてきた人も多いと思われます。(少なくとも、私は、20代の頃に、そのように周りから教え込まれてきました。)
 しかし、VBスクリプトの言語仕様や動作の仕組みは、昔のBASIC言語のそれよりも、JavaやJスクリプトのそれによく似ています。それらの機能限定版とみたほうがよいのかもしれません。そもそもマイクロソフト版のBASIC言語は、C言語やPascal言語のように、字下げによる入れ子方式のプログラミング記法だったので、行番号やGOTO文が無くても、プログラムが書けました。しかも、そのことは、プログラムのモジュール構造化をできるようにして、オブジェクト指向プログラミングへと発展しました。
 また、一昔前までは、ウィンドウズAPIなどをシステムコールとして呼び出していたものが、今ではオブジェクトの実体を作ってそれを活用するというふうに変わってきました。VBスクリプトなどの新しいBASIC言語では、システムコールを直接呼ぶ代わりに、ウィンドウ・オブジェクトというものを活用できるように、Set文とかNew命令とかClass文とかが、その言語仕様内で使えるようになっています。(なお、これらの命令や文は、既存のオブジェクトを利用するだけでなく、自前で新たにオブジェクトを作るのにも使えます。)
 問題は、ある程度実力のあるプログラマが、昔のBASICの言語仕様に捕らわれて、その呪縛から抜け出せられないでいることだと思われます。今さらBASICでプログラムを組むなんて、気恥ずかしい。あれは、初心者用のプログラミング言語だ。プロが使ったら馬鹿になる。というイメージが濃いのだと思います。(その割には、大きなシステムのバグ不具合で社会的に大きな事故が時折テレビのニュースになったりします。)
 実際には、実務上いろいろと事情があるとは思います。その一方で、現在の私は、そうした実務からは遥か遠く離れて、一銭にもならない趣味でコンピュータ・プログラミングをやっています。「何らかの技術をパクる。」と言うと、人聞きが悪いと思われるかもしれませんが、私には一銭の儲けにもなっていないことをここで断言させていただきます。今回私がパクったのは、『共用体』そのものではなくて、その『共用体』の役に立ちそうな側面の一つをVBスクリプトで形にしたに過ぎません。ひょっとしたら、そのVBスクリプトによるコーディングは、疑似的なプログラムであって、本物のプログラムとか、その実体は別にあるのかもしれません。仮想コンピュータならぬ、仮想プログラムなのかもしれません。あるいは、仮想ソフトウェアなのかもしれません。このことに関して、私自身は、コンピュータのプログラムとかソフトウェアとかは、もともとそのような(実ではなくて虚の)ものだと承知しております。
 いろいろとごったくを並べてしまいましたが、説明に入りましょう。『共用体』とは、具体的にC言語上で、

union EXAMPLE {
int value;
char str[4];
} sample;

などと表せます。『共用体(union)』の定義内{ }の変数の、メモリ上の先頭アドレスは同じになります。すなわち、sample.valueとsample.strは、メモリ上の同じ番地を指しています。前者は、そのメモリ番地から始まる4バイトで一つの整数型のデータ(数値)をC言語のプログラム上で扱えるようになります。後者は、前者と同じメモリ番地から4バイトで半角文字4つ分の文字データ(文字列)を同じくC言語のプログラム上で扱えるようになります。
 これだけ見てもわかるように、『共用体(union)』は、メモリの節約や有効利用にしか役に立たない、というC言語プログラマの主張に嘘が無いことがわかると思います。それはそれで正しいとして、私がやりたいのはC言語風に書くと次のようなことです。

union DIMENSION {
int R(63);
int Q(7,7);
} Board8x8;

 変数領域Board8x8とは、縦8マス横8マスのゲームのボードを表します。その各マス目に、白石あるいは黒石あるいは空きのいずれかの状態を記憶します。ここで、各intの変数(4バイト)はcharの変数(1バイト)のほうがメモリの節約になります。が、それほど大きなメモリ領域ではないため(256バイトくらい)、ここではメモリの節約は考えないことにします。
 これに何のメリットがあるのかを説明いたしましょう。1次元配列でアクセスできるメモリ領域を2次元配列でもアクセスできると、便利なことがあります。たとえば、囲碁や将棋やチェスなどの盤面を、コンピュータのメモリ上にデジタル化(つまり、オブジェクト化)したい場合を考えてみましょう。ゲームの盤面を一括してクリアしたり、くまなく検索したりする場合は、一つのカウンタやインデックスで一次元配列をアクセスするほうが処理は速いし、プログラムも効率的です。一方、一つか二つの盤上の箇所だけをチェックしたり変更したい場合は、縦y値と横x値(xy座標値)で2次元配列をピンポイント・アクセスする方が、速くて効率的です。
 しかるに、『思考ゲームプログラミング −オセロゲームのアルゴリズムと作成法−』という本で、『森田オセロVer.6.1』のC言語によるプログラムリストをみてみると、ゲームの盤面を表すメモリ領域は全て1次元配列になっていました。縦y値と横x値(xy座標値)で1次元配列にアクセスする前に、それら2つの値を計算して1次元配列上のポイント位置に変換する処理が必ず入っていました。そのたんびに変換するための計算をしなければならないため、C言語のプログラムがやや複雑になっていました。
 その変換のための計算を、プログラムを作る私の側でやりたくないがために、私は今回の技(わざ)を考え出したわけです。それでは、それをVBスクリプトとDynamicHTMLによるウィンドウ・オブジェクトを駆使して、どのように実現したかを以下に述べることとしましょう。まずは、その中核部分を次のプログラム例で示します。

Option Explicit
Dim Q(7,7), R(63), idx, yy, xx

idx = 0
For yy = 0 to 7
    For xx = 0 to 7
        Set Q(yy, xx) = R(idx)
        idx = idx + 1
    Next
Next

 この例での注意点は3つあります。配列変数の添え字は、0(ゼロ)スタートだということです。この例では、配列変数Qの各要素を表す添え字には0から7まで、配列変数Rの各要素を表す添え字には0から63までの値が使えます。また、便宜上、xy座標値を(縦y値,横x値)の順番で、かつ正の整数値をとりうるものとして表記しています。もう一つ注意することは、先頭アドレスが同一のメモリ領域をアクセスするのに、Q(7,7)とR(63)の二つの配列変数(あるいはメモリ領域)を確保するという点です。これはどいうことかと申しますと、配列変数Qには、Set文によって配列変数Rの各要素をポイントするアドレス値が入ります。すると、VBスクリプト上で、配列変数Qを使うと、配列変数Rの各要素を間接的に使えるようになります。すなわち、Q(0,0)とR(0)、Q(1,1)とR(9)、Q(3,2)とR(26)、Q(7、7)とR(63)などの要素は、VBスクリプト上では同じ要素の内容として扱えるということです。
 実体としては、配列変数Qには、配列変数Rの各要素をポイントしているアドレスがずらっと並んでいるだけです。アドレス値だけのテーブル、すなわち、ベクターテーブルみたいなものを、VBスクリプトのプログラムによって自前で作ってみたわけです。
 Qの配列要素(y、x)=Rの配列要素(y*8+x)
というふうに疑似的な数式で表現できる関係を、いちいちQからRへのyとxの変換値として計算することも、もちろんプログラム上では可能です。しかし、ゲームの盤面の操作のように、あちこちの処理でそうした計算の必要不要を検討していると、結局面倒なことになります。(少なくとも、それが面倒だと言うのが私の意見です。)
 さらに、これを具体的にDynamicHTMLでウィンドウ・オブジェクトとつなげて、縦横8x8のマス目の盤面を仮想的に作ってみましょう。

<SCRIPT language="VBScript">
Option Explicit

Sub Window_Onload
    Dim yy, xx, idx

    idx = 0
    For yy = 0 to 7
        For xx = 0 to 7
            Set R(idx) = Document.all.tags("SPAN")(idx)
            Set Q(yy, xx) = R(idx)
            idx = idx + 1
        Next
    Next
End Sub
</SCRIPT>

<BODY>
<DIV class=banmen >
<SPAN id=P00 ></SPAN> <SPAN id=P01 ></SPAN>
<SPAN id=P02 ></SPAN> <SPAN id=P03 ></SPAN>
<SPAN id=P04 ></SPAN> <SPAN id=P05 ></SPAN>
<SPAN id=P06 ></SPAN> <SPAN id=P07 ></SPAN><BR>

<SPAN id=P10 ></SPAN> <SPAN id=P11 ></SPAN>
<SPAN id=P12 ></SPAN> <SPAN id=P13 ></SPAN>
<SPAN id=P14 ></SPAN> <SPAN id=P15 ></SPAN>
<SPAN id=P16 ></SPAN> <SPAN id=P17 ></SPAN><BR>
    :
   (途中、省略)
    :
<SPAN id=P70 ></SPAN> <SPAN id=P71 ></SPAN>
<SPAN id=P72 ></SPAN> <SPAN id=P73 ></SPAN>
<SPAN id=P74 ></SPAN> <SPAN id=P75 ></SPAN>
<SPAN id=P76 ></SPAN> <SPAN id=P77 ></SPAN>

</DIV>
</BODY>

 Set R(idx) = Document.all.tags("SPAN")(idx)によって、配列変数Rがウィンドウ・オブジェクト(window.Document)の、SPANタグでくくられた各要素をポイントできるようにしています。実は、ここでは、配列変数Rもベクターテーブル化しているわけですが、VBスクリプト上ではそのことを意識しないで使うことができます。さらに、SPANタグにはID(識別子)も付いているため、すなわち、

Q(7,5).InnerText = "◯"
R(61).InnerText= "◯"
P75.InnerText ="◯" 

のそれぞれ3行、あるいは、

teban = Q(7,5).InnerText 
teban = R(61).InnerText
teban = P75.InnerText 

のそれぞれ3行は、プログラム上では同じ意味の操作となります。
 ちなみに、なぜ1つ1つのSPANタグに個別のID(識別子)を付けるのか、というのを説明します。64個のSPANタグに同じIDを付けて、例えば、

<SPAN=masu ></SPAN>
    :
   (途中、省略)
    :
<SPAN=masu ></SPAN>

としてみましょう。すると、Set Q(yy, xx) = masu(idx)という操作が必要となり、1次元配列変数Rは不要となります。にもかかわらず、そうしないのには理由があります。実は、盤面を人間の側がマウスのカーソルを移動したりクリックしたりするので、イベントドリブン型のプログラム(Document_OnclickとかDocument_OnmouseoverとかDocument_Onmouseoutなど)を記述する必要がありました。そのプログラム処理の中で、マウスのカーソルが動いた盤面マス目の位置とか、マウスがクリックされた盤面マス目の位置を知る必要があります。その際に、各SPANタグに個別のID(識別子)が付いていて、イベントが発生した盤面マス目の位置が個別にわかるようにしてあるわけです。現在の私のプログラミング・スキルでは、その程度の仕掛けを作るので精一杯というところです。もっといい方法もあるかもしれませんが、それは他の実力あるプログラマに任せることといたしましょう。
 もちろん、こうした技術全体は外国由来のものであり、私のしていることはオリジナリティに欠けることかもしれません。しかし、そうであったとしても、私がこうしたことに意欲を掻き立ててしまうことにはそれなりの理由があります。やはり、コンピュータという機械をプログラムというもので自前で動かしてみたいという、強い意欲があるからだと思います。