投稿
おちゃめ ochame_nako
DEF命令を使って自作関数(自作命令)を作ったことがある人も多いと思います。 そこで、DEFについて知っておいた方が良いことを少しだけ長々と書いておきます。 DEF X2(A)  RETURN A*2 END 見ての通り関数X2は2倍した値を返す関数です。 PRINT X2(123)とすると当然ながら246という値を返します。 では、PRINT X2("123")とした場合の動作はどうなるでしょうか?(そもそもエラー無しで動く?) 初心者の人もぜひ考えてみてください。
12そうだね
プレイ済み
返信[1]
親投稿
おちゃめ ochame_nako
DEFで定義している関数X2の引数に使用しているローカル変数Aは数値変数なので文字列を与えたら「Type mismatch」エラーが出ると思う人もいるでしょう。 しかし、実際は"123123"という値を返し、ローカル変数Aは文字列変数として解釈されています。 つまり、引数に使われる変数は記述した時点では型が決まらず、代入された時点で決まるということです。 X2関数を次のように書き換えました。 DEF X2 A OUT B  B=A*2 END この場合は戻り値であるBも引数Aによって型が決まります。 したがって、引数がX2 123 OUT B のように引数が数値である場合は戻り値を入れる変数は数値変数にする必要があります。 同じ関数で引数を文字列にした場合には戻り値は文字列になるため X2 "123" OUT B$ のように戻り値には文字列変数を使う必要があります。
1そうだね
プレイ済み
返信[2]
親投稿
おちゃめ ochame_nako
この関数を「Aが正の数のみ2倍にする」(負数の場合は0を戻す)という関数にしてみましょう。 DEF X2 A OUT B  IF A>0 THEN B=A*2 END ここで X2 -1 OUT B とするとType mismatchエラーとなります。 引数が数値変数であるため戻り値も数値変数であるため問題ないはずですが、代入された時点で型が決まるため引数に負数を設定したらローカル変数Bには値が代入されてないため型が決まらず戻り値に数値変数を指定しているためType mismatchエラーとなるわけです。 エラーが出ないためにはローカル変数Bが数値変数で確定させる必要があり、最も簡単な方法はB=0をDEF内で実行させるということです。
0そうだね
プレイ済み
返信[3]
親投稿
おちゃめ ochame_nako
DEF X2 A OUT B  B=0  IF A>0 THEN B=A*2 END これで、ローカル変数Bは数値変数であることが確定します。(実数型か整数型かは確定しない) B=0の時点で数値変数が確定するため引数に文字列を入れるとB=A*2の部分でType mismatchエラーとなります。
0そうだね
プレイ済み
返信[4]
親投稿
おちゃめ ochame_nako
最初に書いたX2関数は数値も文字列も B*2 という処理を行いましたが、数値と文字列とで異なる動作をするような関数を作れば非常に自由度が高い関数になります。 例えば私が作ったBTWAIT命令では BTWAIT "AB" と記述すればAボタンとBボタンを両方押すまで待つという処理を行い、BTWAIT 48 と記述(Aボタン=16、Bボタン=32なのでA+Bは48)してもそれと同じ動作を行ってくれます。 ちなみに引数に代入されたのが文字列か数値かは私が作ったSUFFIX関数で判別可能です。(BTWAIT命令やSUFFIX関数については私の活動参照) 「文字列と数値の比較演算では3を返す」というのを利用してinfとnanを駆使して判定を行うよりも圧倒的に短いプログラムになっています。
0そうだね
プレイ済み
返信[5]
親投稿
おちゃめ ochame_nako
初心者にとってDEFというのはハードルが高いかもしれないですが、「こんな命令(関数)があったらいいな」というのも工夫次第でいくらでも作れてしまいます。 例えばver.3.1.0で新たに加わったシステム変数HARDWAREでNew3DSか旧3DSのどちらで動作させているのが分かるようになりましたが、私はプチコン3号が発売された日にそれと同等の機能を持つNEW3DS関数を作りました。 DEF NEW3DS()  VAR M=MAINCNT  FOR I=1TO 25000:NEXT  RETURN MAINCNT-M<3 END このNEW3DS関数での機種判別の原理は見ての通りすごく単純です。 自分しか使わないだろうという命令や関数であっても一度作っておけば他のプログラムに流用が簡単にできます。
0そうだね
プレイ済み
返信[6]
親投稿
おちゃめ ochame_nako
サブルーチンとして作ってGOSUBで使用するという旧来のBASICの方法もありますが、やっぱりローカル変数やローカルラベルが使えて戻り値が設定できるのがDEFを使うメリットです。 ローカル変数が使えばそのプログラム(DEF以外の部分)で使っている変数名を気にせず変数を使用することができます。 DEF内ではローカル変数かグローバル変数の両方が使用できDEF内で定義したかDEF外で定義したかで使い分けることができます。 DEF内ではすべてローカルラベルとなっているためDEF内でジャンプする際にはラベルの重複を考えずにラベル名を付けることができます。 ただし、DEF内から外部のラベル(グローバルラベル)にはアクセスできないことに注意が必要です。
0そうだね
プレイ済み
返信[7]
親投稿
おちゃめ ochame_nako
というわけで、初心者の人も難しく考えすぎずにDEFを使ってみてください。 新しい境地が開けると思います。 初級~中級者の人も今回書いたことを活用すればDEFで作った自作関数が不可解なエラー、不可解な動作をすることもなくなり、よりDEFを深く活用できるのではないかと思います。
3そうだね
プレイ済み
返信[8]
親投稿
ちーたけ hello.world256
DEF X A OUT B X = B * 2 - 1 END PRINT X(12) 'OUT :: 23
0そうだね
プレイ済み
返信[9]
親投稿
おちゃめ ochame_nako
ちーたけさんへ このX関数において変数Bの型が未確定(Bへの代入処理が行われていない)なので実行するとType mismatchエラーが発生すると思います。 X(12)として戻り値が23になるようにするならば「X=B*2-1」の部分は「B=A*2-1」になりますね。 恐らく上記の私のコメントにおいて「この場合は戻り値であるBも引数Aによって型が決まります。」という部分の書き方が悪かったのかもしれませんが「この場合」というのは「関数内で引数Aを用いてBへの代入処理を常に行っている場合」にはAが数値変数の時は自動的にBも数値変数になるという意味です。 次のコメントにおいて書いている「常にBへの代入が行われるとは限らない場合」の対比としてあえてそのような書き方をしています。
0そうだね
プレイ済み
返信[10]
親投稿
ちーたけ hello.world256
あっ誤字です。 「X=」->「B=」です。 お恥ずかし~。
0そうだね
プレイ済み
返信[11]
親投稿
ちーたけ hello.world256
あっあっ違う。 更に上乗せで誤字でしたうわあ。 「X = B * 2 - 1」がおちゃめさんの書いた通りになります。 何でこんな初歩的なミスを犯すんだろう。 まあ要するにOUTの帰り値が1つの場合はX OUT B == X()が等価ってことが言いたかったのです。
0そうだね
プレイ済み
返信[12]
親投稿
ちーたけ hello.world256
帰り値の変数の型が未定なのは既知でした。 だって多分実装としては名前渡しですもの。
0そうだね
プレイ済み
返信[13]
親投稿
おちゃめ ochame_nako
ちーたけさんへ 私が書いた2つのX2関数は前者が戻り値が1つの場合に等価になる例、後者が等価にはならない例として記しています。
0そうだね
プレイ済み
返信[14]
親投稿
ちーたけ hello.world256
DEF RET2(A,B) RETURN A RETURN B 'どうやっていっぺんにかえりち2つもかえすってんだいこのすっとこどっこい!! END RET2 12,13 OUT ??? ですもんね。 今までX2辺り見てませんでした、返り値の変数は未代入時はnullっぽいのでinitializeしないと型のあれこれでエラー吐いちゃいますもんね。 SUFFIX命令には尊敬してました。
0そうだね
プレイ済み
返信[15]
親投稿
おちゃめ ochame_nako
とりあえず、上記のうち1点のみ訂正しておきます。 「DEF内から外部のラベル(グローバルラベル)にはアクセスできない」 というのは正しくはなく「可能ですが、非推奨」というだけです。 GOTO "0:@A"とすればスロット0のグローバルラベル@Aへとジャンプできます。 指定スロットではなく現在動作中のスロットへのジャンプがしたいのであれば自作関数SLOTを使いGOTO STR$(SLOT())+":@A"とすれば良いです。 ただし、DEF内では必ずENDを実行する必要がありますがDEF外からDEF内へはジャンプは不可能なのでDEF外にジャンプするプログラムを書いてしまうとスタックをどんどん消費していってstack overflowとなります。 GOSUBならばDEF外にジャンプしてもRETURNによってDEF内に戻れるのですが、正しく戻らないと同じくstack overflowとなります
0そうだね
プレイ済み
返信[16]
親投稿
おちゃめ ochame_nako
ちなみに本トピックス内で使用されているSUFFIX関数、SLOT関数はこちらのトピックスをご覧になってください。 SUFFIX関数 https://miiverse.nintendo.net/posts/AYMHAAADAAB2V0fSfxRUkA SLOT関数 https://miiverse.nintendo.net/posts/AYIHAAAEAACHVRTs1c4kFA
0そうだね
プレイ済み