HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2010
0624
木村皆様、処理の長いswitch構文をどう始末していますか?14解決


木村

リンク

2010/6/24(Thu) 00:41:17|NO.33400

 switch構文は便利なのですが、caseの個数と処理する内容が増えると文章量が爆発的に
増大して可読性と保守性が恐ろしく低下してしまいます。(※[1]参照)
 皆様はこの問題をどうやって解消しておられるのでしょうか? 教えてくだされば幸いです。

//[1] :流れを分からなくする最長不倒switch構文 ;本処理 switch msg case "1st" /*―――――長々しい処理コード―――――*/ swbreak case "2nd" /*―――――長々しい処理コード―――――*/ swbreak /* ――――――――――――――――――――――――― ―――――非常に長々しいn個のcase文の羅列――――― ――――――――――――――――――――――――― */ swend



この記事に返信する


木村

リンク

2010/6/24(Thu) 00:42:13|NO.33401

 私はまずcase内の内容を#deffuncプリ命令に置き換えて、本処理で記述する部分を
switch〜case〜swendのみに抑えるようにします。(※[2]参照)
 これならcase内の処理がいくら重かろうとも本処理の行数が水膨れする事はありませんが
やはりcaseの数だけ本処理の行数が増えてしまいます。

//[2] :caseの数だけ本処理の行数が増えてしまう ;本処理 switch msg case "1st" : 1st_function : swbreak case "2nd" : 2nd_function : swbreak /* 以下n個の長々しいcase文の羅列 */ swend ;命令部分 #deffunc 1st_function /*長々しい処理コード*/ return #deffunc 2nd_function /*長々しい処理コード*/ return /* 以下n個の長々しい#deffuncプリ命令の羅列 */



木村

リンク

2010/6/24(Thu) 00:43:42|NO.33402

 そこで、あらかじめswitchのcaseに当たるメッセージ集を文字列型配列変数に、
処理の行き先集をラベル型配列変数に入れておいて、本処理ではrepeat〜loop構文を利用して
switch構文を実装してみます。(※[3]参照)
 これなら、処理もケースもどれだけ増えようが本処理はわずか3行で済みます。……もっとも、
その代償としてシステム全体としては行数が水膨れしてしまっているのですが……

//[3] :本処理は美しいけれど…… ;下拵え sdim メッセージ集,32,n : ldim ラベル集,n /*以下n個のメッセージとラベルの対応を設定する*/ ;本処理 repeat n if msg=メッセージ集.cnt : gosub ラベル集.cnt : break loop ;ラベル部分 *1stラベル : 1st_function : return *2ndラベル : 2nd_function : return /*以下n個のラベル〜return構造の羅列*/ ;命令部分 #deffunc 1st_function /*長々しい処理コード*/ return #deffunc 2nd_function /*長々しい処理コード*/ return /* 以下n個の長々しい#deffuncプリ命令の羅列 */



木村

リンク

2010/6/24(Thu) 00:45:15|NO.33403

 もし命令を代入できる変数型があれば、[3]の水膨れも少しは収まるでしょう。(※[4]参照)
 ですが、そんな利用価値の薄い変数型が登場する訳も無いですし、やはり[2]辺りで妥協すべき
なのでしょうか。(ちなみに、わざわざサブルーチンから命令に飛んでいるのは、一命令内で
ローカル変数が増殖してしまう事を防ぐ為です)

//[4] :命令を代入できる変数型があるならば…… ;下拵え sdim メッセージ集,32,n : odim 命令集,n /*以下n個のメッセージと命令の対応を設定する*/ /*※odimはOrderDimention、命令を代入する型の変数を作れるだろう幻の命令*/ ;本処理 repeat n if msg=メッセージ集.cnt : 命令集.cnt : break loop ;命令部分 #deffunc 1st_function /*長々しい処理コード*/ return #deffunc 2nd_function /*長々しい処理コード*/ return /* 以下n個の長々しい#deffuncプリ命令の羅列 */



info

リンク

2010/6/24(Thu) 09:51:20|NO.33410

自分はこんな感じです。

case 前には わたくし定義 の識別子
この場合は
;<----処理の一言説明---->
を書き込んで、色的、に場所分けをします。

本文は


case msg func_hoge1 func_hoge2 func_hoge3 swbreak

の様にして、処理の始めと終わりを解りやすくします。

タブを編集するのは面倒ですが、その分解りやすくなると勝手に思っております。


#enum PID_RUN = 0 #enum PID_STOP #enum PID_DRAW #enum PID_CLS #enum PID_EXIT #enum PID_TOTAL *@ pid = rnd( PID_TOTAL ) gosub*label_ProcSwitchMain wait 1 goto*@b *label_ProcSwitchMain switch pId ;<----実行の為の処理----> case PID_RUN Proc_Running@Global 12 swbreak ;<----停止の為の処理----> case PID_STOP gosub *label_Proc_Stop swbreak ;<----描画処理----> case PID_DRAW DrawBackGround@Global 0xFF00FF swbreak ;<----クリア----> case PID_CLS cls // 〜長ったらしくない処理〜 swbreak ;<----終了処理----> case PID_EXIT end // 〜長ったらしくない処理〜 swend return // 引数が有る場合は#deffunc #deffunc DrawBackGround@Global int dbgg_coloref_ 〜長ったらしい処理〜 return #deffunc Proc_Running@Global int prg_flg_ 〜長ったらしい処理〜 return // 引数が無い場合は*label *label_Proc_Stop 〜長ったらしい処理〜 return



テック

リンク

2010/6/24(Thu) 21:41:26|NO.33416

>皆様はこの問題をどうやって解消しておられるのでしょうか? 教えてくだされば幸いです。
木村さんは、switch文の条件分岐を文字列で行っていますが、
文字列ではないといけない理由などありますか?
なければ整数で管理すれば、
if msg=メッセージ集.cnt : 命令集.cnt : break
で条件判断しなくてもよくなるかと。

以下にサブルーチンがラベルのサンプルスクリプトを記載しておきます。

#include "hspdef.as" ldim g_lstLabel, 10 g_nLabelIndex = 0 // ラベル配列を初期化する g_lstLabel.0 = *Label0 g_lstLabel.1 = *Label1 g_lstLabel.2 = *Label2 g_lstLabel.3 = *Label3 g_lstLabel.4 = *Label4 g_lstLabel.5 = *Label5 g_lstLabel.6 = *Label6 g_lstLabel.7 = *Label7 g_lstLabel.8 = *Label8 g_lstLabel.9 = *Label9 // 乱数発生を初期化する randomize // 10回ループする for i, 0, 10, 1 // 0〜9までの乱数を発生させる g_nLabelIndex = rnd(10) // 乱数に該当するラベルを呼ぶ gosub g_lstLabel.g_nLabelIndex next stop // ラベル0 *Label0 mes "Label0" return // ラベル1 *Label1 mes "Label1" return // ラベル2 *Label2 mes "Label2" return // ラベル3 *Label3 mes "Label3" return // ラベル4 *Label4 mes "Label4" return // ラベル5 *Label5 mes "Label5" return // ラベル6 *Label6 mes "Label6" return // ラベル7 *Label7 mes "Label7" return // ラベル8 *Label8 mes "Label8" return // ラベル9 *Label9 mes "Label9" return

行き先は、木村さんと同じくラベル型配列変数に格納しておき、
呼び出したいラベルのIDを整数型変数で管理しておけば、
gosub ラベル型配列変数.整数型変数名
で処理できるかと。

またサブルーチンが、#deffuncや#defcfuncなど
自作命令や自作関数のほうがいいというならば、
プラグインを使用しなければなりません。
HSP3用拡張プラグイン「hpi_exprogctrl」というのがあり、
(http://hp.vector.co.jp/authors/VA043120/hpi_exprogctrl.htm)
ユーザー定義命令・関数の関数ポインタ(アドレス)を取得するという機能があります。
上記のプラグインで取得した関数ポインタ(アドレス)を配列に格納しておき、
callfunc命令で呼び出すことができないかと私は考えています。

しかし、「hpi_exprogctrl」は、
以下のバージョンしか対応していないみたいなので注意してください。
HSP 3.0, HSP 3.0a, HSP 3.1b1 - b7, HSP 3.1b9 - b10
(HSP 3.1b8 では動作しません。)



チェ

リンク

2010/6/25(Fri) 14:11:05|NO.33427

こんなんは?


;条件分岐用の文字列 str_array.0 = "label0" str_array.1 = "label1" str_array.2 = "label2" ;分岐先のラベル label_array.0 = *label0 label_array.1 = *label1 label_array.2 = *label2 ;分岐用の文字列 example = "label1" ;分岐処理 repeat 3 if example == str_array.cnt : gosub label_array.cnt loop stop ;ラベル達 *label0 mes "label0だよ。" return *label1 mes "label1だよ。" return *label2 mes "label2だよ。" return



lltakashill

リンク

2010/6/26(Sat) 17:48:44|NO.33441

goto/gosubを使ってみるのは、どうでしょうか?



木村

リンク

2010/6/27(Sun) 14:40:58|NO.33459

 まずは回答してくださった皆様に感謝します。

>>lltakashill様
 gosub命令はともかく、goto命令をどうやってswitch構文の簡略化に用いるのでしょうか?
教えてくだされば幸いです。

>>チェ様
 [3]の実作型のソースですね。問題はrepeat構文の部分とラベル達の部分とでローカル変数等を
共有していなければならないという点です。果たしてどうしたものでしょうか……

>>info様
 C辺りだとcase次行〜break間を字下げするみたいですね。
 最後のcase時にはswbreakを入れずとも良いというのは新発見でした。ありがとうございました。

>>テック様
 これは素晴らしいです。整数にする事でswitch構文が不要になるとは嬉しい限りです。
 hpi_exprogctrlも嬉しい拡張プラグインでした。ただ、callfunc系列の命令はどれも引数の
型を統一しなければならなかったので、サブルーチンを自作命令に変換する仕組みは自前で
作ってみました。



木村

リンク

2010/6/27(Sun) 14:43:45|NO.33460


;―――――――――――――――――――――――――――――――――――――――― ; setlabf1 labeldim(%1),strdim(%2),case(%3),func(%4),parameter1(%5) ;―――――――――――――――――――――――――――――――――――――――― ; (labeldim,strdim)の変数の組にcaseとfuncとの対応を投入するマクロ。 ; このマクロでは引数が1つの命令しか取り扱えないが、以下のsetlabf4を作る ;要領で引数が任意の場合のsetlabf系マクロを作る事ができる。 ; setlabf系マクロは同一の(labeldim,strdim)の変数の組を利用できるので ;引数の数の異なる命令を一つの変数の組に対応させる事ができる。 ;―――――――――――――――――――――――――――――――――――――――― #define setlabf1(%1,%2,%3,%4,%5) \ %txf1 goto *%i : \ %txf2 *%i : %4 %5 : return : \ %txf1 *%o : if vartype(%2)!2 { sdim %2,64,1 } %txf2 if vartype(%1)!1 { \ %2(0)=str(%3) : ldim %1,1 : %1=*%p \ } else { \ %2.length(%1)=str(%3) : %1.length(%1)=*%o \ } #define setlabf4(%1,%2,%3,%4,%5,%6,%7,%8) \ %txf1 goto *%i : \ %txf2 *%i : %4 %5,%6,%7,%8/*ここの引数の数が%4の命令の引数の数に対応する*/ : return : \ %txf1 *%o : if vartype(%2)!2 { sdim %2,64,1 } %txf2 if vartype(%1)!1 { \ %2(0)=str(%3) : ldim %1,1 : %1=*%p \ } else { \ %2.length(%1)=str(%3) : %1.length(%1)=*%o \ } ;―――――――――――――――――――――――――――――――――――――――― ; msgswitch labeldim(%1),strdim(%2),msg(%3) ;―――――――――――――――――――――――――――――――――――――――― ; (labeldim,strdim)の変数の組に対して、msgと同じcaseがあるかを判別するマクロ。 ; msgがcaseと同じだった場合、対応させたfuncが発動する。 ; 一応switch構文の拡張として作ったつもり。 ;―――――――――――――――――――――――――――――――――――――――― #define msgswitch(%1,%2,%3) \ foreach %2 : \ if labelstr.cnt=%3 { gosub %1.cnt } \ loop ;―――――――――――――――――――――――――――――――――――――――― ; colormes atc(%1),r(%2),g(%3),b(%4) ;―――――――――――――――――――――――――――――――――――――――― ; 自作マクロ等も利用できる事を説明する為だけのマクロ ;―――――――――――――――――――――――――――――――――――――――― #define colormes(%1,%2,%3,%4) color %2,%3,%4 : mes %1 //caseを入力 setlabf4 label,labelstr,"Fire",colormes,"炎",255,0,0 setlabf4 label,labelstr,"Ice",colormes,"氷",0,0,255 setlabf4 label,labelstr,"Wind",colormes,"風",0,255,0 setlabf4 label,labelstr,"Thunder",colormes,"雷",128,0,255 setlabf4 label,labelstr,"Earth",colormes,"土",255,128,0 setlabf4 label,labelstr,"Light",colormes,"光",0,192,192 setlabf4 label,labelstr,"Darkness",colormes,"闇",192,0,192 //switch構文に対応 msgswitch label,labelstr,"Thunder" msgswitch label,labelstr,"Darkness"



木村

リンク

2010/6/27(Sun) 14:49:06|NO.33461

 setlabf系列がcaseに、msgswitchがswitch文に対応しています。
 皆様のレスのおかげで自分なりに納得できるswitch文の圧縮ができました。重ねて
ありがとうございます。

 ……ただ、この方式だとcaseに使いたい命令の引数の数に応じてsetlabf系列をいくつも
作らなければなりません。
 贅沢な話ですが、setlabf系列を一本化する方法(=引数を調整できる命令の書き方)は
無いものでしょうか?



info

リンク

2010/6/30(Wed) 00:58:41|NO.33503

自分の知る限り、#define マクロの引数の数を可変にするのは無理だったと思います。

とりあえず自分なりに考えてみました。

マクロの引数に 命令本文をぶち込んでいます。

引数内の命令文の区切りに[ : ] 記号は使えません。そこで引数が終わりだと判断してしまうみたいです。
そのため使用する命令は
#define で 下のスクリプトのように少しいじってやる必要が有ります。

むしろ、ここまでするのなら 普通に case break を使った方が良いような気がします。


#define ctype colorcb(%1=0,%2=0,%3=0) :color %1 , %2 , %3: #define ctype mescb(%1="") :mes %1: #define ctype gosubcb(%1) :gosub %1: #define ctype dimcb(%1,%2=0,%3=0,%4=0,%5=0) :dim %1,%2,%3,%4,%5: #define ctype dialogcb(%1="",%2=0,%3="") :dialog %1,%2,%3: #define ctype logmescb(%1="") :logmes %1: #define ctype case_break(%1,%2) case %1:%2: swbreak #define ctype default_break(%1,%2) default :%1: swbreak test = 3 switch test case_break( 0 , colorcb( 255 ) mescb( "test1" ) gosubcb( *label1 ) dimcb(var) ) case_break( 1 , colorcb( 255,255,0 ) mescb( "test2" ) gosubcb( *label2 ) dimcb(var) ) case_break( 2 , colorcb( 255,0,255 ) mescb( "test3" ) gosubcb( *label3 ) dimcb(var) ) case_break( 3 , colorcb( 0,255,255) mescb( "test4" ) gosubcb( *label4 ) dimcb(var) ) default_break( dialogcb("どれでもない") mescb("取りあえずどれでもない") logmescb("やっぱりどれでもない") ) swend stop *label1 mes "label1" return *label2 mes "label2" return *label3 mes "label3" return *label4 mes "label4" return



レノス

リンク

2010/6/30(Wed) 02:44:16|NO.33504

構造体があれば…… orz

> info さん
case 〜 swbreak を swbreak : case 〜 の形にしたら、swbreak が省略できます。


#define global xcase swbreak : case #define global xdefault swbreak : default switch ( 0 ) xcase 0: mes "0" : logmes "0" xcase 1: mes "1" : logmes "1" xdefault: mes "?" : logmes "?" swend

> 木村 さん
switch 文を1つの命令にしてみてはどうでしょうか。
実装を隠したので、短くなったように見えます (そう見えるだけですが)。


write "Thunder" // 文字列 "Thunder" に対応する文字を、特定の色で描画 write "Darkness" //-------------- #module #deffunc write str s switch ( s ) xcase "Fire": colormes "炎", 255, 0, 0 // 以下略 swend return #global



木村

リンク

2010/7/1(Thu) 22:40:15|NO.33514

>>info様
 レスポンスありがとうございます。こんな妙な難題に何度も四苦八苦していただいて
恐縮の限りです。おっしゃられる通り、こんな形になるぐらいなら普通にswitch〜case
〜swend構文で始末した方がよっぽどスマートですね。
 ただ、当初はswitch構文の可読性を良くする為にはどうしたら良いかと言う意図だったの
ですが、最近の自分の中ではどうすればswitch構文をもっと拡張性高くできるかと言う部分に
力点が移っており、故にNo,33460のような妙ちくりんな構造を示して、なおかつ引数を可変に
できないかなどと贅沢な質問をしてしまったわけです。申し訳ありません。



(以下は言い訳)

/*本処理ファイルにて*/ ;―――――――――――――――――――――――――――――――――――――――― switch 場合 case "甲" /*長〜い処理甲*/ swbreak case "乙" /*長〜い処理乙*/ swbreak swend ;――――――――――――――――――――――――――――――――――――――――
 こんな処理があったとして、今度新たに『場合が丙の際の処理』を追加したくなったと
します。switch構文だとこの本処理を修正しなければなりません。(この部分の修正だけで
済むとも言えますけど)



木村

リンク

2010/7/1(Thu) 22:46:00|NO.33516


/*ヘッダファイルにて*/ ;―――――――――――――――――――――――――――――――――――――――― setlabf0 label,labelstr,"甲",長〜い処理甲を示す命令 setlabf2 label,labelstr,"乙",長〜い処理乙を示す命令,引数1,引数2 ;―――――――――――――――――――――――――――――――――――――――― /*本処理ファイルにて*/ ;―――――――――――――――――――――――――――――――――――――――― msgswitch label,labelstr,場合 ;――――――――――――――――――――――――――――――――――――――――
 この構造だとマクロのようにヘッダファイルを修正するだけでswitch部分が拡張できます。
 テック様やチェ様のスクリプトが原型なわけなのですが、この方が本処理を修正せずに、
拡張に対応できるような気がしたのです。
 が、No,33461で申した通り、引数が固定の為、引数の数だけマクロを作ると言う何とも
見苦しいやり方になってしまったので、引数を可変にできないかと質問したわけです。



>>レノス様
 やはりswitch構文自体を消す事は不可能そうですね。真なるオブジェクト指向においては
switch構文など単にポリモーフィズムと言う風の前の塵に同じと聞きますが、やはりswitch
構文からの脱出は難しそうです。(真なるオブジェクト指向においてはif構文、for構文さえ
無用らしいのですが、条件分岐及び繰り返しに至っては除去する方法さえ思いつきません)



ONION software Copyright 1997-2021(c) All rights reserved.