AX

電霊ログ

私のやってるサイト、電子の言霊の活動ログ

カテゴリークラウド

タグリスト

最近のトラックバック

月別アーカイブ(タブ)

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  • [HSP超入門] ネストレベル2

前回ネストレベルに付いて書いた時、
repeat の後は必ず continue か break か loop でループを抜ける、
gosub の後は同じように return で戻るらなければいけないと説明しましたが、これらの基本を理解する事で変則的ですが repeat や gosub 中でも goto を使う事が出来る様になります。

どういう事かというとネストは単に深さを知るためだけの物ではないと言う事です。たとえば gosub はどんなに離れた場所へジャンプしようと return を実行すれば必ず元の呼び出された場所に戻って来なければいけません、そのためには元の呼び出し元の位置を記憶しておく必要があります。HSPの内部では単なる重なりだけでなくそれぞれの呼び出し元の位置も一緒に記憶しているのです。
それでなにが分かるかと言うとネスト1にはネスト1の呼び出し元がネスト2にはネスト2の呼びだし元がそれぞれ存在している事になります、そして return を使えばそれに応じた呼び出し元の位置に戻って来る事が出来ます。

と言う事は gosub でサブルーチンジャンプした後に goto でジャンプしたとしても、その goto の先で return を使えば正確に元の位置へ戻してくれると言う事です。
では実際に試してみましょう。
実行するとネストレベルを画面に表示しますが、ずっと1のまま増えていない事が確認出来ます。まさに思った通りの動作で大成功です。

repeat
gosub *label1
wait 50
loop

*label1
goto *label2
stop

*label2
mes sublev
mes looplev
return


このサンプルでは、メインループから gosub で *label1 へサブルーチンジャンプし更にそこから goto で *label2 へジャンプして、その label2 の中から return を実行しています。
さて、この return は一体どこに戻っているのでしょう、return は呼び出し元に戻す命令です、それなら goto のある7行目でしょうか? って違いますね! return のネストに対応した gosub 命令のある2行目に戻って来ています。なので8行目にある stop は実行されません。
(上のサンプルは repeat を使っていますが、もちろん *main ~ goto でも大丈夫です)

repeat 内のからの goto も同じように実行する事が出来ます。

repeat
wait 50
goto *label
loop

repeat
*label
mes sublev
mes looplev
loop


ちょっと見にくいですが、考え方はさっきと同じです。
ただ違うのは *label を repeat loop で囲んでいる事です。repeat loop は単独で存在できない(エラーが出る)でのこれをしないと実行出来ません。もちろん continue や break も出来ます。しかし break の場合は元の場所には戻りません。どうやら自分から見て下方向にある loop を抜けようとするようです。
それともう1つ、今回の場合 wait は goto より上にないと無限ループになりますので注意。

でもちょっとくらい無理が聞くからってこういうのはダメです。

*main
gosub *label
wait 50
goto *main

*label
repeat
if cnt=1 {
mes sublev
mes looplev
return
}
loop


gosub で跳んで repeat に入り適当な条件によって return で元の gosub のあった2行目に戻って来れているので一見これで良い様に見えますが、reoeat を loop,continue,break 以外で離れループから抜け気っていないのでネストレベルが貯まって行きます。

これもダメです。

repeat
wait 50
goto *label1
loop

*label1
gosub *label2

repeat
*label2
mes sublev
mes looplev
loop


1行目の repeat に対しての 13行目の loop は効いていますが、7行目の gosub に対しての return がありませんので sublev のネストレベルが貯まってしまいます。

今回内部の動きを理解するために、こういう無茶で変則的な荒業を紹介しましたが、プログラムが見にくく改良し辛くなる可能性が高くなってしまうので、実行出来るからと言って実験的プログラム以外ではこの様な書き方はなるべくしない方がいいでしょう
スポンサーサイト
  • [HSP超入門] ネストレベル1

ここで、今までに覚えてきた repeat と gosub の注意点に付いて触れておきたいと思います。
それはネストレベルに付いてです。
ネストは前に説明したので分かると思いますが、自分の中に自分と同じ構造の物が入っている状態の事です。
それでネストレベルとは何かと言うと、ネストがいくつ重なっているかを表す物で、ネストの深さとも言われます。
例えば、この下のスクリプトの「ここは?」と書かれた場所のネストレベルは1になります。

repeat 10
mes "ここは?"
loop


そしてこの下のスクリプトの「ここは?」と書かれた場所のネストレベルは2になります。

repeat 10
repeat 10
mes "ここは?"
loop
loop


今やったのは repeat でのネストレベルでしたが、gosub にもネストレベルがあります。
またまた例で見てみましょう。
ここは?と書かれた所のネストレベルは1です。

gosub *label1
stop
*label1
mes "ここは?"
return


そして下のスクリプトでは、ここは?と書かれた所のネストレベルは2になります。

gosub *label1
stop
*label1
gosub *label2
return
*label2
mes "ここは?"
return


もちろん次の様なスクリプトでのネストレベルは0です。

mes "ここは?"


さてここからが本題です、はじめにも言いましたがネストレベルに付いて注意する点があります。
それは何かと言うと、HSPではネストレベルに限界があって、
repeat で31、gosub で126 のネストレベルを超えてはいけないのです。
もしもこれを超えるとエラーが出てしまいます。

前に gosub の説明をした時「gosub は往復切符だから return で戻らないと戻りの切符がどんどんたまって行ってしまう」と書きましたが実はそれがネストレベルだったのです。
gosub命令では gosub が実行された時にネストレベルが1つ上がり、return命令が実行される事で始めてネストレベルが1つ下がります。ですので gosub でジャンプしたにもかかわらず return を使わずに戻ってきてしまうと、ネストレベルが増えたきり下がりません、そしてこれが何回も繰り返されネストレベルの上限を超えてしまった時にHSPが「なんかおかしい事になってるよ」と言う具合でエラーを出します。

repeat命令の時も同じで repeat が実行された時にネストレベルが1つ上がり、loop命令、continue命令、break命令 のどれかで loop を抜けて始めてネストレベルが1つ下がります。ですので repeat を実行したのに loop,continue,break を使わないでループを抜ける様なプログラムを書いてしまうとネストレベルが下がらなくなり、これが繰り返され上限を超えてしまうとHSPがエラーを出します。

これまで「ネストレベル」と一言で片付けて来ましたが、repeat のネストレベルと gosub のネストレベルは別々に管理されていて、お互いのネストレベルが混ざってしまう事はありません。

実際に確かめて見ましょう。
HSPには現在のネストレベルが分かる便利な システム変数が用意されていますので、それを使って見てみる事にします。

まずは looplev です。
looplev は現在の repeat のネストレベルを格納しているシステム変数です。

次に sublev です。
sublev は現在の gosub のネストレベルを格納しているシステム変数です。

(「現在の」と言うのは そのシステム変数が使われた瞬間です)

まず repeat のネストを確認してみます。
ちゃんと loop がありますがその前に goto があって最初の *a に飛ばしているのでこの loop は実行されません。そのためネストレベルが増え続けていきます。

*a
repeat
mes "ループレベル="+looplev+" サブレベル="+sublev
wait 100
goto *a
loop


下のサンプルは gosub のネストを確認します。
4行目の gosub でラベル *b に飛ばした後 return を実行したいところですが、その前にある goto で *a 戻ってしまっているので、永遠に return は実行されません、そのためネストレベルが増え続けます。

*a
mes "ループレベル="+looplev+" サブレベル="+sublev
wait 100
gosub *b
goto *a

*b
goto *a
return


最後に repeat と gosub のネストレベルを同時に確認して見ます。

*a
repeat
mes "ループレベル="+looplev+" サブレベル="+sublev
wait 100
gosub *b
loop

*b
goto *a
return


こうして見ると、repeat のネストレベルと gosub のネストレベルが別の物だという事がはっきり分かりますね、なれない内はこの両方を意識しながらプログラムを書いていくのは大変かもしれませんが「repeat の後は loop か continue か break から抜ける」、「gosub を使ったら return で戻る」という基本的な事をしっかりやっていれば、これらが原因で起こる不具合に悩まされる事は少なくなるでしょう。
と言うよりもこの事実を知っていると言う事が重要なのです。
  • [HSP超入門] ラベル5

かな~り長いこと放置してしまったな~
そろそろ気を取り直して書いて行こうかな。

button 命令を紹介した時に 指定したラベルが存在しない時はエラーが出ると説明しましたが、これは goto や gosub 命令でも同じで、必ずラベルを作ってから使用してください。(今まで言うのを忘れていましたゴメンよ)
たとえばこんな風に、

goto *abc
stop

goto 命令のパラメータに指定したラベルがどこにも無い時にエラーが出ます。
ですのでこの様に、

goto *abc
stop
*abc

goto や gosub そして button 命令などで使用しているラベルは必ず作成してから使いましょう。


ここで「メインルーチン」と「サブルーチン」について説明したいと思います。
ルーチンとは関連性のある まとまった処理の事で、メインルーチンはその名の通りメインのルーチンなのですが、分かりやすくおおざっぱに言うと プログラム全体を管理しているルーチンと言う事になります。
それに対してサブルーチンは、名前の通りサブのルーチンで、メインルーチンなどの他のルーチンから呼び出されて使われるルーチンです。

サブルーチンに付いてはもう少し詳しく説明しましょう、
メインもサブもなく ある程度大きなプログラムを書いていると読みにくいだけでなく、色々な所で同じ様なプログラムを書いてしまっている事があります。
これら共通の処理を1つのまとまりとしておいて、それらの処理が必要な箇所でそのつど共通な処理を呼び出せば、同じ様な処理は1つで済みプログラムサイズが減るだけでなく全体のプログラムも見やすくなり、修正する時も1箇所だけで済むので、プログラミングの負担も軽減されます。それがサブルーチンです。サブルーチンは言い方を変えればプログラムの部品の様な物ともいえますね。
ですので1つのプログラムにつきサブルーチンは複数ありますが、メインルーチンは1つだけです。

HSPではラベルを使う事で、このメインルーチンやサブルーチンを作成する事が出来ます。
では簡単な例で見て行きましょう。まずはメインもサブも無いベタ書きのスクリプトです。

repeat

mes a
a=a+1
if a>=20 {
a=0
b=b+100
pos b,0
}

wait 10
loop

変数aに1ずつ加算していってそれを表示するだけスクリプトです。そして変数aが20になったらaを0に戻して表示位置を横に100づらしています。
これをルーチン分けするとこうなります。

*main

gosub *sub1
gosub *sub2
gosub *sub3

wait 10
goto *main

*sub1
mes a
return

*sub2
a=a+1
return

*sub3
if a>=20 {
a=0
b=b+100
pos b,0
}
return

まず最初に *main と言う名前のラベルがありますね、さっしの通りこれがメインルーチンです。そしてメインの中に gosub 命令が3つありそれぞれ sub1,sub2,sub3 のラベルを順番に呼び出しています、これがサブルーチンです(今までの説明から分かったと思いますが、サブルーチンを呼び出す命令だからgosubという名前なのでした)。
サブルーチンをどこまで細かく分けるかは人それぞれで、もっと細かく分けても大雑把に分けても構いませんが やはりこれもどの位が丁度いいのか研究して見て下さい。
今回は、
sub1 =画面に結果を表示するルーチン、
sub2 =数を加算するルーチン、
sub3 =変数の内容を監視するルーチン
と言う具合に、処理の意味によって無理矢理3つのルーチンに分けました。

ルーチン分けした事によって、最初は全てが1つのまとまった処理の様に書いていた物が、目的ごとに分けられた事によってプログラムの構造がハッキリして来ましたね。これがメインルーチン、サブルーチンの考え方です。(量は逆に増えてしまっていますが、もとともが小さいプログラムなのでそこは目をつむって下さい)

この場では取り合えずサブルーチンのラベルを sub1,sub2,sub3 としていますが、実際に活用する時には処理内容にあったラベル名を付けます。たとえばゲームならデータを読み書きする、load(ロード)やsave(セーブ)などですね。

前半にサブルーチンは「色んな所で共通する処理を一まとめにした物」と書きましたが、別に全てがそうなっている必要はありません。今回の様に目的ごとに分けるだけでも十分使用するメリットはあると思います。
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。