HSPでモジュール型変数入門

HSPのモジュール型変数の例や説明が少ないなぁと感じることが多かったので、しがない趣味グラマーですが書いてみます。

# モジュール型変数とは
モジュール型変数とは、自分専用のフォーマットで変数や命令、関数などをまとめて格納しておける箱みたいなものです。
この箱の中に変数等をしまいこんでおくことでいつでもモジュール型変数を介してアクセスすることができます。
このしまい込んでおく変数のことはモジュール変数と言います。
**モジュール型変数とモジュール変数は表記は似ていますが全くの別の概念であるため混同しないように注意してください。**
**モジュール型変数**は**<u>モジュール型</u>**の変数で、
**モジュール変数**は**<u>モジュール内で使用する</u>**変数です。

## クラスとの関係(分かる人だけ)
モジュール型変数は他のプログラミング言語でのクラスという概念に近くそれぞれ次の用語で置き換えて考えられます。

モジュール≒クラス
モジュール型変数≒インスタンス※1
モジュール変数≒フィールド、メンバ変数
モジュール命令・関数≒メソッド、メンバ関数、プロパティ(getter、setter)※2

※1 モジュール型変数はvartypeにおいての型名ではstruct(構造体)とされている。
※2 プロパティ用の書式は存在しない。

また、クラスとの違いとしては次のような点が挙げられます。

* モジュール変数で定義する変数は全てprivateであり、setter、getter無しでは値にアクセスすることができない。
* モジュール変数で定義しない変数は全て静的(static)でpublicな変数となる。
* モジュール命令・関数から同モジュール内のモジュール命令・関数を呼ぶ場合も第一引数にモジュール型変数が必要(同モジュール内からであればシステム変数thismodに格納されるのでそれを引数に渡す)。
* クラスにおける継承みたいなことは出来ない。
    * [やろうと思えばできないこともないみたいです。](https://qiita.com/yosgspec/items/61d8812dcb82af12229d)

# カウンターを作ってみよう
ここでは例としてボタンを押して行くたび1ずつ加算されるカウンターを作ってみます。
従来の機能で普通にカウンター作ろうと思ったら次のようなプログラムになると思います。

```clj:
;カウンタを0に
count=0
button gosub "かうんとっ",*counter
stop

*counter
	pos 0,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 0,30,20,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	count++				;カウンターのカウントを+1
	mes count			;カウンターの値を表示
return
```
このプログラムにモジュール型変数を適用してみます。
モジュール変数に設定したい値は#module文のモジュール名の後にコンマ区切りで続けます。
今回はcount変数をモジュール型変数を用いて定義するので次のようになります。

```clj:
#module countModule count

#global
```
モジュール変数の初期代入はコンストラクタと言われる初期化命令内で行います。
\#modinitで定義を行います。

```clj:
#module countModule count
	;初期化命令
	#modinit
		;カウンタを0に
		count=0
	return
#global
```
モジュール型変数に含まれる値(モジュール変数)はモジュール命令・関数といった専用の命令・関数でないとアクセスすることができません。
これは\#modfunc、\#modcfuncで定義します。
\#deffunc、\#defcfuncと同様にcを含むかどうかで見分けるといいかもしれません。
今回は\#modcfuncでインクリメントをしてカウンタの値を返してみることにします。

```clj:
#module countModule count
	;初期化命令
	#modinit
		;カウンタを0に
		count=0
	return

	;モジュール関数
	#modcfunc nextCount
		count++
		return count
#global
```
これで簡単なモジュールは完成。
最初のプログラムに組み合わせてみます。

```clj:
#module countModule count
	;初期化命令
	#modinit
		;カウンタを0に
		count=0
	return

	;モジュール関数
	#modcfunc nextCount
		count++
		return count
#global

button gosub "かうんとっ",*counter
stop

*counter
	pos 0,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 0,30,20,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	mes	;カウンターの値を表示　何を表示する?
return
```
まだ、countModuleを呼び出していないため、このままで動かすことはできません。
ここで実際にモジュール型変数を作ってみることとしましょう。
モジュール型変数はnewmod命令で定義します。構文は次のようになります。

```bat:
newmod p1,p2[, p3, ...]
p1: モジュール型変数として扱う変数名
p2: 呼び出すモジュール名
p3以降: コンストラクタ(初期化命令)に渡す引数
```
これを実際に組み込むと次のようになります。

```clj:
#module countModule count
	;初期化命令
	#modinit
		;カウンタを0に
		count=0
	return

	;モジュール関数
	#modcfunc nextCount
		count++
		return count
#global

button gosub "かうんとっ",*counter
;モジュール型変数の定義
newmod myCounter countModule
stop

*counter
	pos 0,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 0,30,20,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	mes	;カウンターの値を表示　何を表示する?
return
```
そして、countModuleモジュール内に定義したモジュール関数を呼び出してみます。
こちらは普通の関数を呼ぶ時と一緒で第一引数にモジュール型変数を渡さなければならないこと以外は普通の関数と変わりません。
これは命令についても同様です。

```clj:
#module countModule count
	;初期化命令
	#modinit
		;カウンタを0に
		count=0
	return

	;モジュール関数
	#modcfunc nextCount
		count++
		return count
#global

button gosub "かうんとっ",*counter
;モジュール型変数の定義
newmod myCounter,countModule
stop

*counter
	pos 0,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 0,30,20,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	mes	nextCount(myCounter)	;カウンターの値を表示
return
```
ちなみに、モジュールをプログラムと組み合わせる場合は実際に処理を行うところよりも前に配置するようにしてください。
でないとエラーが起きて動作できなくなることがあります。

## これの利点は何? -> 部品の再利用が簡単にできる!
モジュール型変数を使用すると同じような動作を行う物を簡単にいくつでも呼び出せるようになります。

```clj:
#module countModule count
	;初期化命令
	#modinit
		;カウンタを0に
		count=0
	return

	;モジュール関数
	#modcfunc nextCount
		count++
		return count
#global

button gosub "かうんとっ",*counter
newmod myCounter,countModule
pos 80,0
button gosub "かうんとっ2",*counter2
newmod myCounter2,countModule
pos 160,0
button gosub "かうんとっ3",*counter3
newmod myCounter3,countModule
pos 240,0
button gosub "かうんとっ4",*counter4
newmod myCounter4,countModule
pos 320,0
button gosub "かうんとっ5",*counter5
newmod myCounter5,countModule
stop

*counter
	pos 0,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 0,30,20,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	;モジュール関数の呼び出し
	mes	nextCount(myCounter)	;カウンターの値を表示
return

*counter2
	pos 80,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 80,30,100,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	;モジュール関数の呼び出し
	mes	nextCount(myCounter2)	;カウンターの値を表示
return

*counter3
	pos 160,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 160,30,180,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	;モジュール関数の呼び出し
	mes	nextCount(myCounter3)	;カウンターの値を表示
return

*counter4
	pos 240,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 240,30,260,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	;モジュール関数の呼び出し
	mes	nextCount(myCounter4)	;カウンターの値を表示
return

*counter5
	pos 320,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 320,30,340,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	;モジュール関数の呼び出し
	mes	nextCount(myCounter5)	;カウンターの値を表示
return
```
※分かりやすさ重視でコピペコードにしています。

# モジュール関数の値をラップして(包んで)みる
先程作ったモジュール関数はカウントを1ずつの加算しかできません。
では、元のモジュール関数を消さないで2ずつ加算されているように見せかけるにはどうしたらよいでしょうか?

答えとしては\#defcfunc文を組み合わせることがベストなように考えられます。(\#define文でも可)
モジュール型変数を受け取った\#defcfuncを経由して\#modcfuncにアクセスします。
このとき受け取るモジュール型変数は便宜上thisと名付けておきます。
※\#defcfunc側ではvar型としてしか受け取れないので注意。

その場合は別のモジュール関数で包んで処理を加えてあげるといい感じです。システム変数のthismodには現在のモジュール型変数本体が入っているので、これを第一引数に加えてnextCountを呼ぶとそれの値が返ってきます。(\*2017/1/18追記:F1ヘルプに載ってるの知らなかった…)

```clj:
#module countModule count
	;初期化命令
	#modinit
		;カウンタを0に
		count=0
	return

	;モジュール関数
	#modcfunc nextCount
		count++
		return count

	;モジュール関数をラップする
	#modcfunc nextCounts
		return nextCount(thismod)*2
#global

button gosub "かうんとっ",*counter
newmod myCounter,countModule
pos 80,0
stop

*counter
	pos 0,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 0,30,50,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	;ラップしたモジュール関数
	mes	nextCounts(myCounter)	;2刻みでカウンターの値を表示
return
```
nextCounts関数に引数指定できるようにすれば、倍率も指定できるようになります。

```clj:
#module countModule count
	;初期化命令
	#modinit
		;カウンタを0に
		count=0
	return

	;モジュール関数
	#modcfunc nextCount
		count++
		return count

	;モジュール関数をラップする
	#modcfunc nextCounts int n
		return nextCount(thismod)*n
#global

button gosub "かうんとっ",*counter
newmod myCounter,countModule
pos 80,0
stop

*counter
	pos 0,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 0,30,20,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	;モジュール関数の呼び出し
	mes	nextCounts(myCounter,5)	;5倍でカウンターの値を表示
return
```

入口の説明としてはこんなものですかね? 
delmod

僕がQiitaにあげたプログラムで[これ](http://qiita.com/yosgspec/items/5c7a8954fa5555593b21)とか[これ](http://qiita.com/yosgspec/items/e4287262f8dbea2aa815)はモジュール型変数を使用していますので良ければご覧ください。

# 余談
HSPのインスタンス生成(モジュール型変数定義)がどうも気持ち悪いのでこんな感じにしてやるといい感じかなぁとか。

```clj:
#module countModule count
	;初期化命令
	#define new(%1) newmod %1,countModule
	#modinit
		;カウンタを0に
		count=0
	return

	;モジュール関数
	#modcfunc nextCount
		count++
		return count

	;モジュール関数をラップする
	#modcfunc nextCounts int n
		return nextCount(thismod)*n
#global

button gosub "かうんとっ",*counter
new@countModule myCounter
pos 80,0
stop

*counter
	pos 0,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 0,30,20,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	;モジュール変数の定義
	mes	nextCounts(myCounter,5)	;5倍でカウンターの値を表示
return
```
あとは実際に使ってもらいたい関数以外はlocalで隠したり、デフォルト引数込みのdefineでラップしたりしてます。

```clj:
#module countModule count
	;初期化命令
	#define new(%1) newmod %1,countModule
	#modinit
		;カウンタを0に
		count=0
	return

	;モジュール関数 localで隠蔽(publicなので呼べることは呼べる)
	#modcfunc local nextCount
		count++
		return count

	;モジュール関数をラップする
	#modcfunc local nextCounts int n
		return nextCount@countModule(thismod)*n

	;defineでデフォルト引数付けてさらにラップ
	;(同名でglobal化するなら関数→defineの順で定義すること)
	#define global ctype nextCounts(%1,%2=8) nextCounts@countModule(%1,%2) 
#global

button gosub "かうんとっ",*counter
new@countModule myCounter
pos 80,0
stop

*counter
	pos 0,30			;カウンターの表示位置
	color 200,200,200	;カウンターの背景を灰色に
	boxf 0,30,20,50		;カウンター表示のリセット
	color 0,0,0			;文字の色は黒
	;モジュール関数の呼び出し
	mes	nextCounts(myCounter)	;デフォルト倍率でカウンターの値を表示
return
```