|
|
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プリ命令の羅列
*/
|
|
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
|
|
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系列を一本化する方法(=引数を調整できる命令の書き方)は
無いものでしょうか?
|
|
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構文さえ
無用らしいのですが、条件分岐及び繰り返しに至っては除去する方法さえ思いつきません)
|
|