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


HSPTV!掲示板


未解決 解決 停止 削除要請

2011
0618
zx構造体の利便性・必要性13解決


zx

リンク

2011/6/18(Sat) 22:57:34|NO.39714

おそらく構造体に関しては、これまで幾度も議論なり要望なり行われたものと存じますが..

例として、ゲーム等で使用される選択肢を実装する場合、アイテムには表示テキストと処理内容の二つのパラメータが必要になりますが、
これをHSPで実装する場合、おおかた以下のように組まれると思います。

screen 0, 200, 115
title "spaceで決定" sdim select_texts, 64 dimtype select_lavels, 1 i = 0 select_texts.i = "アイテム1" select_lavels.i = *onselect_item1 i++ select_texts.i = "アイテム2" select_lavels.i = *onselect_item2 i++ select_texts.i = "アイテム3" select_lavels.i = *onselect_item3 i++ select_texts.i = "アイテム4" select_lavels.i = *onselect_item4 i++ select_sum = i *main color : boxf stick key, 0 if (key && 16) : gosub select_lavels.active_select if (key && 2) : active_select-- if (key && 8) : active_select++ active_select = limit(active_select, 0, select_sum - 1) repeat select_sum if (cnt == active_select) { color 0, 0, 200 } else { color 100, 100, 100 } pos 10, 10 + 25 * cnt boxf ginfo_cx, ginfo_cy, ginfo_cx + 120, ginfo_cy + 20 color 255, 255, 255 pos ginfo_cx + 20, ginfo_cy mes select_texts.cnt loop wait 10 goto *main *onselect_item1 title "item1" return *onselect_item2 title "item2" return *onselect_item3 title "item3" return *onselect_item4 title "item4" return

この例では選択肢の各アイテムのインデックスに、テキスト配列とラベル配列を対応させることでパラメータを割り当てています。
パラメータ毎に配列を用意しプレフィックスを与えることで管理するという、言うまでもない一般的な方法かと存じます。
ただこの方法には変数が増え変数名も冗長になるという欠点があります。

他にも二次元配列を無理やり構造体に見立てて実装するやり方もありますが、
この場合はHSPの配列の仕様上、型の異なる値を含めることができないのと、可視的にパラメータを判断できない等の不都合からマイナーな手法かと存じます。

いずれにせよ共通して言えることは、構造体が無いなりにみなデータを工夫して管理しているという点です。

しかしながら、こういった工夫や管理はコーディングに長けた上級者にこそ向き、初心者には不便・困難なのではないかというのが私見です。

配列が一つの型に依存しないようになり、添え字に文字列(または文字列をキーのエイリアスに)指定できればいいのですが、
さすがにこれでは大きな仕様改変になるので、この先に構造体型変数なるものができれば良いなあと思っています。

仮に上記の例が以下のように書ければとても便利だと思います。
selects.i.text = "item"
selects.i.lavel = *on_select

このように、異なる型を混同でき自由に識別子を付けられる構造体は、
初級者から上級者までもが便利に活用でき、HSPのコンセプトにも背かないと思われる魅力的な概念であると思います。

というわけで、今後構造体に近似した機能が実装される予定があるなら知っておきたいし、
ないならここで改めて強く要望したい次第です。



この記事に返信する


木村

リンク

2011/6/19(Sun) 00:39:08|NO.39716

 正直、それ位ならモジュール変数で十分対応できる気がします。以下は一例

screen 0, 200, 115 title "spaceで決定" item = 0 #define set_item_macro(%1,%2) newmod item, m_Item \ : label = %2 \ : set_item item(length(item)-1), %1, label set_item_macro "アイテム甲", *onselect_item甲 set_item_macro "アイテム乙", *onselect_item乙 set_item_macro "アイテム丙", *onselect_item丙 set_item_macro "アイテム丁", *onselect_item丁 #undef set_item_macro *main stick key, 0 if (key && 16) : cmd_item_gosub_label item(active_select) if (key && 2) : active_select-- if (key && 8) : active_select++ active_select = limit(active_select, 0, length(item) - 1) redraw 0 color boxf repeat length(item) if (cnt == active_select) { color 0, 0, 200 } else { color 100, 100, 100 } pos 10, 10 + 25 * cnt boxf ginfo_cx, ginfo_cy, ginfo_cx + 120, ginfo_cy + 20 color 255, 255, 255 pos ginfo_cx + 20, ginfo_cy mes get_item_name(item(cnt)) loop redraw 1 wait 10 goto *main *onselect_item甲 title "甲:元ネタは尻尾の付いた亀" return *onselect_item乙 title "乙:元ネタはジグザグ" return *onselect_item丙 title "丙:元ネタは脚の張り出した台" return *onselect_item丁 title "丁:元ネタは釘" return #module m_Item 名称, 処理 #modinit 名称 = "" 処理 = *未定義 return #modfunc set_item str name, var label 名称 = name if vartype(label) = 1 { 処理 = label } else { 処理 = *未定義 } return #modfunc cmd_item_gosub_label gosub 処理 return #modcfunc get_item_name return 名称 *未定義 return #global
 むしろアイテムのような、各個体の構成要素が種類によって大きく変わるデータ構造の場合、構造体よりモジュール変数の方が使い勝手が良い気がします。何故なら、モジュール変数は各要素をポインタとして扱っているようなので、各要素毎に全く違う構造のデータを保有する事ができるからです。

/*C構造体の場合*/ struct item { char name; int price; int kind; struct weapon { int kind; int attribute; int power; int weight; } arm; struct recover { int scope; int effect; } drug; };

/*モジュール変数の場合*/ #module m_Drug 範囲, 効力 #global #module m_Arm 種類, 属性, 威力, 重量 #global #module m_Item 名称, 価格, 種類, アイテムの特有効果 #global
 個人的にはnewmod命令で新規に獲得した配列の番号がstat辺りに返ってくれると非常に助かります。
 引数0のnewmod命令でとりあえず実体だけを作っておいて、中身の作成は#modfunc命令で作った命令に任せる事が多いので、newmod命令で作られた実体の場所を探す必要がある為です。



ひらまる

リンク

2011/6/19(Sun) 01:39:42|NO.39717

>個人的にはnewmod命令で新規に獲得した配列の番号がstat辺りに返ってくれると非常に助かります。
ほんとそうですよねー;
自分は、以前この掲示板で教えてもらった

#module #defcfunc getModuleID var In_module, local Out_ID mref Out_ID, 1 return Out_ID #global #module TestModule ThisID #modinit ThisID = getModuleID( thismod ) return ThisID #global newmod ModuleVar, TestModule mes stat newmod ModuleVar, TestModule mes stat newmod ModuleVar, TestModule mes stat
を使っていますが、
クラスや構造体を実装しないのならば、モジュール周りを強化して欲しいという願望はありますね。
HSPLetやHSPコンバーターでもモジュール変数は使えないので不便に感じています。



GENKI

リンク

2011/6/19(Sun) 01:57:57|NO.39718

お。なんてタイムリー。
ちょうど関連した事をHSP開発wikiで考えてたんです。(↓コメ欄)
http://hspdev-wiki.net/?hsp3%2F%A5%E2%A5%B8%A5%E5%A1%BC%A5%EB%CA%D1%BF%F4

標準では配列取得命令ないから削除のとき困るんですよね。
なのでこんな風にやってみたんですが…こんな事してるのサンプルとかでも見たことないからちょっと不安。

#module mod_test prm1 ;------------------------ ; コンストラクタ ; モジュール変数に値を登録 #modinit count++ prm1 = count * 10 mes "set:" + prm1 return ;------------------------ ; デストラクタ ; モジュール変数を開放したときの処理 #modterm mes "削除しました。" return ;------------------------ ; 内容取得 #modfunc mod_get return prm1 ;------------------------ ; 条件を満たすモジュール変数を削除 #modfunc mod_del int pp if prm1 = pp : delmod thismod return #global ;################################################# ;モジュール変数作成 repeat 3 newmod a, mod_test loop gosub *disp ;内容表示 ;条件を満たすモジュール変数を削除 foreach a mod_del a(cnt), 20 ;中身が20なのだけ消す loop gosub *disp ;内容表示 ;追加 newmod a, mod_test gosub *disp ;内容表示 stop ;################################################################ *disp ;内容表示 mes "-<内容確認>---------------------------------" foreach a mod_get a(cnt) mes "num("+cnt+"):" + stat loop mes "----------------------------------------------" return
モジュール変数で配列番号が取得できないのは使うな・気にすんなって事だと思うんですよ。



ひらまる

リンク

2011/6/19(Sun) 12:20:49|NO.39721

>モジュール変数で配列番号が取得できないのは
モジュール内のthismodを関数の第一引数として渡して、
mref 変数A, 1
とすると、変数Aにモジュールの配列番号が代入されるんですよ!(↑参照)



GENKI

リンク

2011/6/19(Sun) 13:46:02|NO.39725

> mref 変数A, 1
> とすると、変数Aにモジュールの配列番号が代入されるんですよ!(↑参照)

> 標準では配列取得命令ないから削除のとき困るんですよね。

過去ログなんかでも見かけていたので知ってはいました。
正確には「標準命令では専用の命令が用意されていない」という意味で書いたつもりでいました。誤解を招きすみません。
代替手段がありながらどうして説明も専用命令も無いんだろうということでした。



xxx

リンク

2011/6/19(Sun) 20:44:26|NO.39745

> mref 変数A, 1
> とすると、変数Aにモジュールの配列番号が代入されるんですよ!(↑参照)

次のコードを実行してみてください。


#module MTest id #modinit mref id, 2 return id #global max = 5 dimtype mod, vartype( "struct" ), max repeat max newmod mod, MTest : id = stat mes "id = " + id await 0 loop

配列番号はmref 変数, 2で取得できます。



ひらまる

リンク

2011/6/19(Sun) 21:19:16|NO.39747

>配列番号はmref 変数, 2で取得できます。
すごぉぉい!!
超便利かつベリーイージーじゃないですか…!
しかしここまで簡単だと逆に、GENKIさんが言ったように、
>代替手段がありながらどうして説明も専用命令も無いんだろう
というのが気になりますね…
thismodというシステム変数があるのですから、
thisidのようなシステムIDがあっても良いと思うんですけどね…

とはいえ、とってもイージーな方法を教えていただきありがとうございました(・ω・v



zx

リンク

2011/6/20(Mon) 02:50:24|NO.39758

アイテムというのは、リストボックスのような多項目からなる一つ一つの項目を示す名称の意味で使わせていただいた単語でした。

木村さんに掲示して頂いた例は、確かに機能として使えないことはありませんが、
パラメータ毎にアクセッサが必要となる仕様は、あらゆるデータの読み書きが頻繁に行われるゲームにおいて不便なものではないかと思うのです。
そして不便なだけという問題なら、配列複数+プレフィックスでもあまり変わらない気がするのです。

せめて
item(cnt)->名称

のように、アロー演算子のようなもので直接モジュール内にアクセスできれば良いのですが。 むしろ、これができればほぼ解決するといっても過言ではないです。



ひらまる

リンク

2011/6/20(Mon) 09:08:11|NO.39761

>直接モジュール内にアクセスできれば良いのですが
まっことそうですよね
毎回

#modcfunc getItemName return ItemName
のような関数を書くのは面倒ですからね;

とはいえ、本当はそうして関数を介して入出力を行った方がバグが少ないと言われていますが…
やはり実行速度が気になりますよね; mrefとかでなんとかならないのでしょうか…



ORZ

リンク

2011/6/20(Mon) 10:53:54|NO.39764

mrefによる配列番号取得に目からウロコや涙や眼やにが落ちたのは俺やひらまる氏だけではないでしょう。
ありがとう。ほんとうにありがとう。

自分で何もかも書くのが嫌になったら、こっちに逃げてしまいましょうず
http://sprocket.babyblue.jp/html/hsp_sqlele.htm

実行速度?ゲーム?知らねぇな!!



木村

リンク

2011/6/21(Tue) 06:48:25|NO.39778

>>zxさん
>パラメータ毎にアクセッサが必要となる仕様は、あらゆるデータの読み書きが頻繁に行われるゲームにおいて不便なものではないかと思うのです。
 私はパラメーター毎に関数が必要な仕様がゲーム作成で不便とは思えません。というのも、パラメーターの書き込み用の記述方法と読み出し用の記述方法が分かれている事が、予期せぬパラメーターの改竄を防ぐと思うからです。
 勿論、取得関数を一々作る必要有り、二次要素の読み出しが面倒、文章が長くなる、等の難点は多々あります。しかし、これらの難点は記述レベルの物です。パラメーターの改竄というバグレベルの難点を記述レベルの難点を受け入れる事で除去できるのなら、それほど悪くない選択だと思います。
 まあ、zxさんのおっしゃる通り、オブジェクト指向を目指しているモジュール変数と共に、記述量が少なくて入門者にも分かりやすい構造体の双方が実装されれば問題は無いと思います。
 ……でも、やっぱり個人的には構造体より、クラスっ気のあるモジュール変数の方が便利な気がするんですよね。HSPに毒され過ぎたかなぁ…………


>>ひらまるさん
>モジュール内のthismodを関数の第一引数として渡して、
>mref 変数A, 1
>とすると、変数Aにモジュールの配列番号が代入されるんですよ!(↑参照)
>>xxxさん
>配列番号はmref 変数, 2で取得できます。
 これは素晴らしい。大変便利なものを教えていただきました。ありがとうございます。これでモジュール変数の拡張性がまた一段上がりました。


>>ひらまるさん
>やはり実行速度が気になりますよね; mrefとかでなんとかならないのでしょうか…
 ポインタ経由の有無による実行速度の減衰は微々たるものだと思います。少なくとも、画像処理や音響処理の影響量に比べれば、塵芥と言わないまで巨象と蟻程度の違いがあると思います。
 参考までに、配列変数とモジュール変数の処理の速さを比較したスクリプトを載せておきます。自分の環境下(N=10000,W=25600)では
・書込(普通の変数):327
・書込(モジュール):796
・読出(普通の変数):0
・読出(モジュール):45
と、大した違いにはなりませんでした。
※上の結果を簡単に要約すると書込を約64回する毎に1msの時間の遅れが生じると言う意味
※読出の場合、約667回する毎に1msの時間の遅れが生じると言う意味

#include "d3m.hsp" #define N 10000 #define W 25600 #module m_A f0, f1, f2 #modinit int i0, int i1, int i2 f0 = i0 f1 = i1 f2 = i2 return #modcfunc get_a_0 return f0 #modcfunc get_a_1 return f1 #modcfunc get_a_2 return f2 #global mes "N="+N+",W="+W wait 300 start = d3timer() dim variable, 3, 1 repeat N variable(0,cnt) = rnd(100), rnd(100), rnd(100) if (cnt\W) = 0 : await 20 loop mes "書込(普通の変数):"+(d3timer()-start) start = d3timer() repeat N newmod module, m_A, rnd(100), rnd(100), rnd(100) if (cnt\W) = 0 : await 20 loop mes "書込(モジュール):"+(d3timer()-start) start = d3timer() foreach variable k = variable(0,cnt)+variable(1,cnt)+variable(2,cnt) if (cnt\W) = 0 : await 20 loop mes "読出(普通の変数):"+(d3timer()-start) start = d3timer() foreach module k = get_a_0(module)+get_a_1(module)+get_a_2(module) if (cnt\W) = 0 : await 20 loop mes "読出(モジュール):"+(d3timer()-start) stop



info

リンク

2011/6/21(Tue) 21:46:34|NO.39794

>配列番号はmref 変数, 2で取得できます。
 有難うございます。始めて知りました。
 毎回、内部にIDを設けてループで検索していた 自分としてはとてもありがたいです。

>この先に構造体型変数なるものができれば良いなあと思っています。
 自分も思います。API を使用する際に、色々と面倒くさいので、
 メモリ構造も合わせてほしいです。
 ついでに C のsizeof 的な物もあれば。完璧だと思います。

 



ひらまる

リンク

2011/6/21(Tue) 23:06:29|NO.39799

>木村さん
>参考までに、配列変数とモジュール変数の処理の速さを比較したスクリプトを載せておきます。
検証しようかなぁ…でも時間計測のためだけにスクリプト打つの面倒だなぁ…
なんて思っていたところに、詳細なデータとスクリプト本当にありがとうございます!
自分の環境では、
・書込(普通の変数):297
・書込(モジュール):439
・読出(普通の変数):0
・読出(モジュール):37
でした。
いろいろいじっていて気づいたのですが、dim variable, 3, 1の部分を
dim variable, 3, Nとしないと、配列を拡張しながら代入していくので速度がかなり変わることに気づきました。
さらに、newmodによるモジュールの作成とmodfuncによるモジュールの代入では、速度に数十倍の差があることもわかりました。
勝手ながら、その他いろいろと改造させていただき、
以下のようなスクリプトを組んでみました。問題があったらおっしゃってください。

#include "d3m.hsp" #define global N 1000000 #module m_A f #modinit dim f, N return #modcfunc get_a_f int i0 return f(i0) #modfunc set_a_f int i0, int i1 f(i0) = i1 return #modfunc get_a_Address var o dup o, f return #global mes "N="+N dim variable, N start = d3timer() repeat N variable(cnt) = rnd(100) loop mes "書込(普通の変数):"+(d3timer()-start) newmod module, m_A start = d3timer() repeat N set_a_f module, cnt, rnd(100) loop mes "書込(モジュールへ命令で):"+(d3timer()-start) get_a_Address module, f start = d3timer() repeat N f(cnt) = rnd(100) loop mes "書込(モジュールへアドレスで):"+(d3timer()-start) start = d3timer() repeat N k = variable(cnt) loop mes "読出(普通の変数):"+(d3timer()-start) start = d3timer() repeat N k = get_a_f(module,cnt) loop mes "読出(モジュールから関数で):"+(d3timer()-start) start = d3timer() repeat N k = f(cnt) loop mes "読出(モジュールからアドレスで):"+(d3timer()-start)
実行するとわかっていただけると思いますが、関数での操作は通常に比べて2倍近い時間がかかります。
しかしdup命令によるクローン変数を使うと、速度を限りなく通常変数の操作に近づけることができます。
なんとなく、「dupしたら早いかな〜?」などと思っていたところだったので、この実験はとても有意義なものになりました。
元のソースとモチベーションを提供してくださった木村さん、本当にありがとうございます。



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