トピック
受付中
しんいち stgf1080

Uninitialized variableの謎解明!?

DEF内のGOSUB先で、引数で与えた配列を参照しようとするとダメなのか。 う~ん、DEF内で配列宣言するとOut of memoryの心配しなくちゃならないと思って、配列DEFの外に出したんだけど、これじゃちょっと不便(>_<)
2そうだね
プレイ済み
返信[1]
親投稿
myu314 myu314
そもそもDEF内のGOSUB禁止ですよ。 このプログラムだとGOSUB自体でエラーは出なくて、10行目のRETURNでGOSUBでは無くDEFを抜けることになります。でも返り値が設定されていないので、Uninitialized variabled usedのエラーになります。 DEF FUNC() RETURN END と同じ状況になっています。
5そうだね
プレイ済み
返信[2]
親投稿
一応 FOR I=0 TO LEN(D)-1 D[I]=0 NEXT のように参照する前に代入すれば回避できるのですけどね。 引数は代入する前は、「どの型にも当てはまらない変数」になるみたいですからね。 DEF ABC OUT V RETURN V END みたいな命令を使うと分かります。
3そうだね
プレイ済み
返信[3]
親投稿
しんいち stgf1080
ありゃ~、DEF飛び出さなけりゃ良いと思ってた^^; そして試しに@LPのRETURNをRETURN Lにしてみたら、なんとエラーなくなりました(@_@) #不思議
0そうだね
プレイ済み
返信[4]
親投稿
MIKI ifconfig
何したいのか不明ですが、実行時のメモリ不足が心配なら必要な配列は全て最初からグローバル変数にしとけばいいのでは?
3そうだね
プレイ済み
返信[5]
親投稿
しんいち stgf1080
そうなんですよ、メモリが心配で。それでDEF内宣言の配列をグローバル化してる最中に遭遇した現象でした。 ただグローバル化して、引数で渡さないで直接グローバル名のままDEF内で参照しても良かったんですけど、どうせなら関数汎用的に使えた方が良いかなぁと思いまして。
0そうだね
プレイ済み
返信[6]
親投稿
↑ GOSUBは禁止、という訳ではありませんよ。 「スロット指定無しのGOTO・GOSUBは、DEF外のラベルに飛べない」というだけだと思います。
4そうだね
プレイ済み
返信[7]
親投稿
myu314 myu314
DEF内のGOSUBはエラーにはなりませんが、DEFのRETURNが優先されるため事実上使い物にならなくなってます。RETURNできない=GOTOと一緒なので。正常に使えるのは GOSUB "1:@LABEL" という呼び方でDEF外のラベルにジャンプするケースのみです。こんな中途半端な機能残さずエラーにした方がよかったと思うんですよね。エラー出ないと何を間違えたか把握しづらいし。 というわけで、DEF内のGOSUBをGOTOにすれば、なぜRETURN Lでエラーでなかったか理解できると思います。できればRETURN Sの方が良いと思いますが:p
3そうだね
プレイ済み
返信[8]
親投稿
MIKI ifconfig
>RETURN Lにしてみたら、なんとエラーなくなりました この理由はすでに myu314 さんが説明された通りです。 >メモリが心配で であれば、文字列操作も実行時にメモリを食い散らかすから、注意したほうがいいですよ。 しょぼーんさん 禁止ではありませんが、 gosub から def 内への return ができないので、 事実上 gosub は使い物にならないということでしょうね。 これはおそらくコンパイラの問題で、 前提として def から戻るコードと gosub から戻るコードは内部的に異なるはずなのですが、 プチコンのコンパイラは def 中 return があったら常に def から戻るコードを生成するのでしょう。 (まあ私だってそうしますww)
2そうだね
プレイ済み
返信[9]
親投稿
MIKI ifconfig
正しくは def 中であっても gosub していたら、return 命令で gosub 戻りのコードを生成しなければならない。 しかしコンパイル時には return 命令が gosub からの戻りなのか def からの戻りなのか決定できない。 ∵goto 使って両者を一つにまとめることができるから 解決するにはコンパイル時ではなく実行時に、return 命令の処理中で、gosub か def かを判断する必要があり実行速度低下に繋がります。
1そうだね
プレイ済み
返信[10]
親投稿
しんいち stgf1080
ふっふっふ、RETURN Sじゃ普通じゃないですかぁ:-p そしてRETURN Lにしても、プログラムの実行結果はSの値になっている、というのが不思議なんです。
0そうだね
プレイ済み
返信[11]
親投稿
あれま、return被りは盲点でした。 失礼しました。
0そうだね
プレイ済み
返信[12]
親投稿
myu314 myu314
あーバグですね、これw (ラベルを除いた)次の行のRETURNが有効になるっぽい。後のRETURNを消すと正常な動作。スタックおかしくなってるのかなぁ?色々まずいことできそうな予感。
3そうだね
プレイ済み
返信[13]
親投稿
MIKI ifconfig
マジかwww これバグレポ出さないで欲しい ∵互換性保ちつつバグ潰すには上記の通り return 命令を遅くするしかないから spset でコード互換性切り捨てた前例もあるから、 def_return とかいう新たな命令を導入するのもアリ てか def 内 gosub とかイラクもといイランだろ
2そうだね
プレイ済み
返信[14]
親投稿
しんいち stgf1080
では黙っとこう(^^ゞ いっそmyu314さんのおっしゃるようにDEF内GOSUB時点でエラーにしてしまっても良いような気もします。
1そうだね
プレイ済み
返信[15]
親投稿
おちゃめ ochame_nako
DEF内でGOSUB~RETURNが使えないのはかなり前から報告が上げられていて社長も知っているはずです。 プチコン3号はメモリとコンパイル速度を稼ぐため恐らく1パスコンパイラであるため対策としてはDEF内ではGOSUBを使えなくするのが最も簡単な解決策です。 すでに書かれているようにDEF内のRETURNはGOSUBの対になるRETURNであってもDEFの値を戻すRETURNとして動作しているわけですが、この場合はGOSUBのスタックが積まれまくってStack overflowになるかというとそうではなくエラーは出ません。
2そうだね
プレイ済み
返信[16]
親投稿
おちゃめ ochame_nako
ちなみにDEF内にGOSUBの対になるRETURNを記述さえしなければ問題ありません。DEF外のラベルにGOSUBでジャンプしてDEF外にあるRETRUNでしっかり戻って動作することは確認済みです。(GOSUBでDEF外にジャンプした際には確実にRETURNで戻ればStack overflowの心配はない) スロット指定をすると特定のスロットでしか動作しないプログラムになってしまいますが、私のSLOT関数を使い指定スロットを「固定スロット」ではなく「現在動作しているスロット」にすればどのスロットに置いても問題なく動作させることが可能になります。 https://miiverse.nintendo.net/posts/AYIHAAAEAACHVRTs1c4kFA
4そうだね
プレイ済み
返信[17]
親投稿
マギー M191246
試してみたら 3.5.2ではSが 3.3.2ではLが返りました。
2そうだね
プレイ済み
返信[18]
親投稿
myu314 myu314
あれ、これ今のバージョンだとDEF内でGOSUB使えるようになってますね…まじか。バグじゃなかった…?いやでもマニュアルとは挙動が異なるけど。 GOSUBからのRETURNの場合でも返り値の有無はチェックされるので最初のコードはエラーになったっぽい。そしてGOSUBの時に返り値設定されるので、もし関数から抜ける時RETURNなくてもUninitialized〜は起きないと。 というわけでいつの間にかGOSUB使えるようになってたが正解だった。
4そうだね
プレイ済み
返信[19]
親投稿
myu314 myu314
返り値セットしとけばRETURNだけでもエラーにならないので、最初のコードならこんな感じで書くのがナウいのでは!なお今後のバージョンで動かなくなる可能性があります:p VAR A[10]:FILL A,2 ?SUM(A) DEF SUM D OUT S S=0 GOSUB @SUB RETURN @SUB VAR I,L=LEN(D)-1 FOR I=0 TO L:S=S+D[I]:NEXT RETURN END
3そうだね
プレイ済み
返信[20]
親投稿
そひ ladixsofiya
DEFを使い始めた頃 DEF内GOSUBにはお世話になったな~。 正常な数値が返らない場合がある事からグローバルとローカルを理解して まともに使える様になった頃に DEF内でDEFを呼び出したらどうなるんだろ~って試した事から DEF内GOSUBが消滅して行く事に。 DEF外送りにする事ばかりだったので そんなバグ仕様には気づかなかったけど ミニPC版も同じ仕様になってたりするのかな~。 余所メーカーのスイッチ版BASICが発表された頃 うちは3DS後継機狙い(社長談)とか言ってたし 修正する側は後々大変そうね。
2そうだね
プレイ済み
返信[21]
親投稿
しんいち stgf1080
あえて逆にDEF外に飛び出して、スロット指定GOSUBで正しく動作させるという手もありましたか。しかもSLOT関数使うことでスロットに縛られないという(発想がすごい)。 今回は単純にDEF内の記述を短くするのにDEF内でGOSUB使ってしまってたので、素直にGOSUB内の記述を展開しようと思います。 #関係ないけどミニMZ80C版のリファレンス #覗いたら、XSCREENの仕様が羨ましいと #ちょっと思いました^^;
1そうだね
プレイ済み
返信[22]
親投稿
しんいち stgf1080
#この次のトピにコメントしようと #思ったらもう閉じてた^^; DEF内GOSUBは修正したんだけど、まだ動作のおかしいところがあって、DEFの引数に配列渡して本当に大丈夫なのかと思って試してたら、大丈夫じゃない場合もあることが分かりました。 下記を実行するとStack overflowになってしまいます。6行目をコメントアウトして代わりに8行目を有効にすると大丈夫。 う~ん、引数やめて直グローバルにするかぁ。
0そうだね
プレイ済み
返信[23]
親投稿
しんいち stgf1080
おっと、3DSじゃ「下記」にならないのか。そして3Dマークがジャマくせぇ^^;(マークの下に隠れてるのは2。何でも良いんだけど)
0そうだね
プレイ済み
返信[24]
親投稿
myu314 myu314
そのStack overflowは無限に自分自身を呼びだそうとして失敗してます。再帰ってやつですね。 DEF FUNC:FUNC:END と同じ状況になってます。8行目の書き方でOKなのは、8行目のSUM()を呼び出す前にSHIFTによって配列の要素が取り覗かれるので、最終的にからっぽになり5行目で終了するため、無限にSUMが呼び出されないからです。3行目と4行目の間に ?"SUM()";LEN(D) とか実行してみるとわかりやすいかと。 ちなみに8行目のやりかたの場合でも、A%の要素数を3000個くらいにするとStackoverflowになるはずです。 というわけで最初のコードも、今回のコードも引数に配列を渡したことが原因ではないので、安心して渡しちゃってください:) ところで、これSUM()実行後、A%が空っぽになるけど大丈夫ですかね?いわゆる参照渡しなので…。
1そうだね
プレイ済み
返信[25]
親投稿
しんいち stgf1080
あ~、恥ずかしい勘違いを(^^ゞ 関数呼び出したあとにSHIFTしてもそりゃあ要素数減りませんよね。 この例はDEFに引数で渡した配列の参照先が、ちゃんといつまでも保持され続けるのかを確かめたかっただけなので、処理内容自体は適当です。 とは言え、この例じゃなくて今作ってるプログラムでは関数渡り歩いてるうちに配列の参照先、どっかで見失っちゃってるような気がするんだよなぁ。 DEF内ローカル配列を引数で渡した配列に代入(1要素じゃなくまるっと)しちゃうとマズそうなのは何となく分かるんだけど、ほか気を付けなきゃならないケースってないのだろうか? #コピー先、元同じCOPYとかARYOPとかは #怪しいんじゃないかと思ってるんだけど。
0そうだね
プレイ済み
返信[26]
親投稿
MIKI ifconfig
copy ringcopy に同一配列を渡すと内容を破壊されることがあります。 当然こちらとしては memmove(3) 的な動作を期待してたんだけどな。 仕様かもしれませんが、まあバグに近い。 ∵システム側で条件分岐するのに比べ、ユーザプログラムで同じ処理するのには莫大なコストがかかる。 def と配列に関しては、 ローカル配列変数を返す def f を、配列を受け取る別の def g にそのまんま渡すとバグることがありましたね。(3.3.2) (var v[n], w[m]: w = f(g(v)) みたいな感じ) 一旦変数を介す事で回避できたので (var v[n], w[m], u[k]: u=g(v): w=f(u) みたいな感じ) 深く追求しませんでしたけど。
1そうだね
プレイ済み
返信[27]
親投稿
MIKI ifconfig
あっ w = g(f()) の誤りでした。 こういうの見ると合成関数 y = f(g(x)) の微分法とか思い出しちゃう。 これがいくらでもネストできると気づいたときの衝撃は大きかったな。
1そうだね
プレイ済み
返信[28]
親投稿
しんいち stgf1080
試した感じ、DEF内でCOPYやARYOP使っても、勝手に配列の参照先がすげ替わるなんてことはないみたい。もしすげ替わってたら、そう記述していたということでしょう(なので動作が変なのは私が間違ってるんでしょう^^;)。 ちなみに、COPY,RINGCOPYの同一配列問題は、前にもちょっと試したことあるんですが、中身が壊れる現象は確認できませんでした(Ver3.5.2)。もしかしたら今のバージョンでは修正されているのか、何か複雑な条件じゃないと再現しないのか。 ローカル配列グローバル化作戦、2次元配列使ってるところがあって、DEFに渡ってからじゃないと次元の要素数決められなくて、どうしようか思案中。DEFに1次元で渡しても、DEF内では2次元で使用できれば便利だったんだけどなぁ(なぜ2次元かというとRINGCOPY用で)。
0そうだね
プレイ済み