Home -> HSP講座 -> 初級編 No.10

数学は得意かい?

こんにちは、上大です。
始めに断っておきますが、数学はたいして関係ありません
でも、知っておいた方がいいに違いありません。


番外:16進数について

普通、人間が使う数字は、0から始まって、9→10となります。
9の次は、一桁あがって10になるんです。これを、10進数といいます。

これの 10 を 16 に置き換えたものが、16進数です。
つまり、16進数とは、「0 〜 15 を1桁で表す」、数の書き方です。

10以降を表す文字には、「ABCDEF」を使います。それぞれ10〜15を表します。
よって、0123456789ABCDEFとなり、その次でやっと位が上がって、10 になります。
ややこしいですね……。

(example)
0F = 15
1F = 31
FF = 255
80 = 128 (※16進数の 80 は「はちじゅう」ではなく「はちれい」と読む)

ちなみに、HSPでは16進数の前に 0x$ を付けて表します。(どちらでもOK)
0x0F(=15)、0xFFFF(=65536)、$8080FF など

次に、数値を10進数に変換する方法です。
16進数は非常に変換しづらいです。
この方法は、N進数の全部で使えます
2進数、3進数、10進数、16進数、32進数……どれも同じ。

まず10進数で考えていきます。
256 という数値は、

2 × 100 + 5 × 10 + 6 × 1
を表します。そうですよね?
同じように、$1C4 という16進数の数値は、
1 × 100(256) + C(12) × 10(16) + 4 × 1(1)
を表します。
16進数の 100 は、10進数では 256 (16 × 16) になります。

まとめると、X桁の N 進数では、

 (0桁目) 0桁目の数 * N0 (右端)
+ (1桁目) 1桁目の数 * N1 (右から二番目)
+ (2桁目) 2桁目の数 * N2 (右から三番目)
…………
+ (X桁目) 左端の数 * NX (左端、右から X + 1 番目)
= 全体の数
これでOKです。

さて、なぜこんなややこしい数字の使い方を知る必要があるのでしょうか?
それはまた今度、機会があれば話します。
今回の講座の内容に直接関係はありませんが、片隅ででも覚えておいてください。絶対にどこかで役に立ちます。


関数を使う

さて、本題スタートです。だいぶ遅くなりましたが……。

関数は、みなさんも使ったことがあるはずです。
よく説明しませんでしたが、rnd() がまさしくそれ、関数です。

というわけで、関数は使いました。


返値があるよ

関数は命令と似ていますが、だいぶ違います。
ほぼ必ずパラメータを持っていて、値を渡さなくてはなりません。
(パラメータに値を指定することを、値を渡すということがあります)
しかも、パラメータは丸括弧 ( ) で囲みます。複数指定するときは、その中で , を使って区切ります。

早いけどおさらいしましょう。

んで、返値ってなによ?

HSPの関数には、必ず返値があります。( C言語などの関数とは違います )
返値というのは、関数が呼び出し元に返してくる値です。
ちんぷんかんぷんですね。(^^;)

返値、戻り値、返り値、リターンバリュー(Return Value)、いろいろ呼び方はありますが、関数は必ずこれを一つだけ返してきます。
これは数値かもしれませんし、文字列かもしれません。

プログラミング的に言うと、数学の関数は、パラメータを使って計算して、戻り値として計算結果を返します。

プログラミングの関数も同じこと、パラメータを元になにか処理して結果を呼び出し元に返してきます。
つまり、戻り値関数の結果、実行結果です。
実行した結果こうなったよというものです。
成功したか失敗したかだけお知らせするものもあります ( あまりいい方法ではありませんが )。


返値を使う

返値を使うには、どうしたらいいのでしょうか?
さて、早速サンプルです。構造化したサンプルです^^。

// rnd 関数の戻り値を使う
#const wID_Main 0	// 必ずウィンドウID は定数にする

*main
	gsel 0, -1
	gosub *init		// 「initialize」には、準備などの意味がある
	gosub *SettingWindow	// ウィンドウをセッティング(準備、整える)
	
	gsel wID_Main, 1
	stop
	
//######## サブルーチン群 ########
*init
	randomize			// 必ず必要
	return
	
*SettingWindow
	screen wID_Main, 320, 240, 2		// 2 で隠しておく
	title "カンソスロット"			// タイトルバーはゲーム名にする
	
	pos 20, 20 : button gosub "push!", *OnSlot
	return
	
*OnSlot
	// ボタンを押されたときの処理
	redraw 2			// ちらつき防止
	
	color 255, 255, 255 : boxf : color
	pos 30, 50
	
	bOK = 1
	
	repeat 3
		nNow = rnd(5) * 2 + 1		// 0 〜 9 の奇数だけにする
		mes "["+ (cnt + 1) +"] = "+ nNow
		
		if ( cnt != 0 ) {			// 最初のループでは無視する
			if ( nNow != nBefore ) {	// 今回の値と前回の値が違ったら
				bOK = 0			// 失敗
			}
		}
		nBefore = nNow		// 今の値を次のループに引き継ぐ
	loop
	
	if ( bOK != 0 ) {
		mes "揃った!"
		if ( nNow == 7 ) {	// [7][7][7]
			mes "スリーセブンだ!!"
		}
	}
		
	redraw 1		// ちらつき防止
	return

無駄に張り切ってしまいました。謎のスロットゲームです。

ちょっと一休みで、このサンプルを解説します。
とりあえず、よく読んで、実行して、動作をよーく観察してください。
何をしているのか分かった上で、答え合わせのつもりでこの解説を読んでください。

goto *解説

*解説
まず、gsel 0, -1 で、元々のウィンドウを消し去ります。
このテクニックは重要です。
これをしないと、*SettingWindow の時の処理が見えてしまいます。

次に、*init ラベルでおきまりの randomize 命令を使います。
後で rnd を使うので、これは必須です。絶対に忘れてはいけません。(o_o;

*SettingWindow で、ウィンドウを準備します。
screen 命令では、非表示のウィンドウを作っています。( p3 の 2 )
button を設置して、終了します。

戻ってくると、前処理(起動直後に行う処理)の最後に、作ったウィンドウを表に出します。
このゲームはボタンを押されたときの *OnSlot ラベルしか使わないので、
stop 命令で止めてしまいます。

*OnSlot はいつもより複雑です。
画面に書き込むので、redraw 命令を使っておきます。
また、boxf で画面全体を真っ白に直します。
毎回 pos でカレントポジションを設定し直すことに注意してください。

次にスロットの部分ですが、その前に「bOK = 1」と代入しています。
これはフラグ (flag) と呼ばれるもので、これが 0 じゃない限り、3つ揃っていることを表します。

「0」はまたは false、「0以外」はまたは true です。
「0」はまたは false、「0以外」はまたは true です。
大事なので、3回いいます。
「0」はまたは false、「0以外」はまたは true です。
これで大切さが伝わったと思います。
このような、ON / OFF の状態を持つ数値を、真理値(boolean)略して bool といいます。
この部分は非常に大切なので、忘れないでください。
一旦 真 にしておいて、違うと分かったら 偽 に変える、という作戦です。
ちなみに、真の変数の値を偽に変えることを、フラグを倒す・おろすといい、
その逆をフラグを立てる・揚げるといいます。

nNow は、現在のループの値、nBefore は前回のループの値です。
二週目からは、これが同じか判断します。
 二週目は [1] と [2] が同じか
 三週目は [2] と [3] が同じか
を、調べます。
三つ揃うなら、( [1] == [2] == [3] ) なので、
( nNow != nBefore ) は成り立ちません。
逆に言うと、どちらかが成り立ったら、三つ揃わないということになります。

nBefore = nNow という代入文は、コメントの通り、値を引き継ぎます。
loop を実行すると、nNow は「前回の値」になるので、nBefore に移し替えます。
(そのときの nBefore は、次に「前々回の値」になるので、残す必要がありません。)

bOK が真のまま ( スロットが三つ揃っている ) なら、if の後の mes を実行します。
次の if はおまけです。

さて、戻り値の受け取り方は、わかりました。
関数自身が、値のように扱えるのです。


関数が使えるタイミング

HSPでは、関数を使う場所に制限があります! zawazawa...

「戻り値を使う」でちらっと言いましたが、関数は戻り値の場所に記述するので、「値」を書けない場所には、関数を書けません。
よって、

	rnd(20)

などは出来ません。
「↑が出来ない!」って悩むことがたびたびありましたけど、それは

	"Hello, world!"

のようなスクリプトが無茶だいうのと同じです。

関数を実行したいだけの場合でも、

	funcret = rnd(10)

のように、代入文の形にしてください。
※変数 funcret は使い捨てます。

また、空の if 文に入れてしまうという無茶苦茶なことも出来ます。

	if ( rnd(5) == 0 )	// 無意味なif文

※必ず == 0 などにしてください。文字列が返ってきたときエラーになります。

これなら条件によらず、(関数以外) なにも実行されません。
もし条件が成立しても、実行する処理が無いからです。
しかし、とりあえず rnd 関数は実行されます。
(rnd 関数の場合は、こんなことしても意味がないですが……)

通常は、戻り値を持つ関数は、動作をしません。
動作(スロットを回したり、変数の値を変更したり。厳密に言うと、副作用を起こすこと)は、命令・サブルーチンですべきであって、関数は戻り値を得るために使うからです。


標準関数を使う

さて、すこし練習して終わりにしましょう。
標準関数を使ってみます。

// 標準関数 - その一

	s = "Hello"		// str    型
	d = 3.141592		// double 型
	i = 2			// int    型
	
	// 変数の型を調べる
	mes "変数 s は "+ vartype(s) +" 型です。"	// str
	mes "変数 d は "+ vartype(d) +" 型です。"	// double
	mes "変数 i は "+ vartype(i) +" 型です。"	// int
	
	// 変数の型を変換
	// int(), double(), str() などというそのまんまの関数
	t = int(d)		// int 型にして t に代入
	mes "変数 t は "+ vartype(t) +" 型です。"
	
	// 文字列の長さを取得
	mes "変数 s は、長さ "+ strlen(s) +" の文字列を持っています。"
	mes "\t内容は["+ s +"]です。"
	mes "文字列 \"あ\" の長さは "+ strlen("あ") +" です。"
	stop

vartype()は、変数の型を数値にして返します。

vartype()の戻り値と型
値 ( 型タイプ値 )
1 label
2 str
3 double
4 int

※これ以降 ( 5 〜 7 ) は当分使わないので、省略。

通常は、1 〜 4 のような数値を直接使わず、vartype("str") のように、型名から値を取得します。

型変換用の関数は、自分でいじくってみてください。

strlen 関数は、文字列の長さを返す関数です。ただし、全角文字は2文字として扱います。(仕様)
後、エスケープ・シーケンスは忘れないでください。\t はタブ文字です。


他にもたくさん知って欲しい関数がありますが、使うときに説明します。
ヘルプを見て予習したら、いいですね。

ではまた次回。

by 上大

第九章へ   第十一章へ