プレイ日記
おちゃめ ochame_nako
今回はDEFで自作関数を作る時の変数の型について書いておきます。 中上級者向けの知識ですが、初心者において「DEFで自作関数を作っていて挙動がおかしいけど何が原因かは分からない」という場合の解決方法も記しているためためぜひ読んでみてください。
14そうだね
プレイ済み
返信[1]
親投稿
おちゃめ ochame_nako
ず、最初に覚えておくべきことはDEFの引数の変数は代入された時点で型が決まるという点です。 例えば値を2倍する関数X2は下記のようになります。 DEF X2(A)  RETURN A*2 END では、X2(123)としてAが123となるならば戻り値は246ですが、X2("ABC")の場合は"ABC"*2なので戻り値は"ABCABC"となります。 変数名Aには文字列変数のサフィックスとなる"$"は付いてないのですが文字列変数が代入された場合は文字列変数として動作するということです。 この辺は以前下記のトピックスにも書いた通りです。 https://miiverse.nintendo.net/posts/AYMHAAACAAADVHjYtPmLxg
2そうだね
プレイ済み
返信[2]
親投稿
おちゃめ ochame_nako
では、数値が与えられた場合はそれを2倍した値を返し、文字列ならばnanを返すようにしてみましょう。 そのためには引数の変数が数値か文字列かを判断しなくてはなりません。それには私の自作関数SUFFIXを用いると簡単です。 自作関数 SUFFIX() https://miiverse.nintendo.net/posts/AYMHAAADAAB2V0fSfxRUkA DEF X2(A)  IF SUFFIX(A) THEN   A=A*2  ELSE   A="nan"  ENDIF  RETURN A END このようにSUFFIXを使えば与えられた型によって動作が変わる自作関数を作ることが容易にできます。
0そうだね
プレイ済み
返信[3]
親投稿
おちゃめ ochame_nako
数値にも整数型と実数型の2つの型があります。 これは引数に使うのが実数型変数か整数型変数かで変わってきます。 例えばこの自作関数X2をDEF外においてB=X2(A%)として使用した場合にはDEF内のローカル変数Aは整数型変数として処理されB=X2(A#)として使用した場合には実数型変数として処理されます。(OPTION DEFINTを使用してない場合はデフォルトの変数は実数型となる) では、変数ではなく定数でこの関数を使用した場合の挙動はどうなるかというとX2(1)だとローカル変数Aは整数型、X2(1.0)だと実数型となります。現在は2倍する関数なので戻り値はどちらも2ですが、戻り値を1.5倍するようにA=A*1.5に変更した場合はX2(1)は1を返し、X2(1.5)は1.5を返します。
0そうだね
プレイ済み
返信[4]
親投稿
おちゃめ ochame_nako
これはOUTを使った場合にも注意しなくてはなりません。 例えばAの値が正数の時に2倍した値を返し、負数の時は0を返す関数X2を下記のように記述したとします。 DEF X2 A OUT B  IF A>0 THEN B=A*2 END この関数ではAの値が0以下の場合は戻り値Bには代入されないためBの型が決定できずエラーとなってしまいます。 そこで下記のようにしました。 DEF X2 A OUT B  B=0  IF A>0 THEN B=A*2 END
0そうだね
プレイ済み
返信[5]
親投稿
おちゃめ ochame_nako
以前はこれを正解例としましたが、実は正しいとは言い切れないのです。 それはBに0を代入した時点で型が決まるためです。 実は0というのは整数型であるためB=0の時点で戻り値の変数Bの型は整数型になります。 Bを実数型にしたいという場合はB=0.0とする必要があるのです。 とはいえ、B=0とB=0.0のどちらかが正解になるというものではなく「整数型として動作させたいのか」「実数型として動作させたいのか」という制作者側の判断でどちらが適切なのかが決まります。 実数値で返すようにしたい(整数に丸められて欲しくない)という場合にはB=0.0にする必要があるということです。
0そうだね
プレイ済み
返信[6]
親投稿
おちゃめ ochame_nako
では、上記の自作関数X2を正数の場合は1.5倍、負数の場合はそのままの値を整数型で返すという場合にはどうなるかを考えてみましょう。(例えばX2(3.6)ならば3.6を整数値にした3を1.5倍にして得られた4.5をさらに整数にした4を返すという感じ) DEF X2 A OUT B  B=FLOOR(A)  IF A>0 THEN B=B*1.5 END これでBの初期値はAを整数にした値、Aが正数ならばその値を1.5倍になっているので想定しているような動作になりそうですが、X2 3.6 OUT Bとしてみると変数Bの値は4.5になります。 これはFLOORでは整数型ではなく単なる整数値であるためです。 分かりやすく言えばA%=3.6においてA%は3という整数型の値になりますが、A#=FLOOR(3.6)においてA#は3という実数型の値(3.0と同じ値)になっているのと同じ状態なのです。
0そうだね
プレイ済み
返信[7]
親投稿
おちゃめ ochame_nako
この場合だと下記のように記述すると想定しているような動作になります。 DEF X2 A OUT B  B=A>>0  IF A>0 THEN B=B*1.5 END シフト演算、ビット演算は値を整数値で返すだけではなく整数型として返すので覚えておくと良いでしょう。(A>>0、A<<0、0 OR A、-1 AND A、NOT-A+1、A DIV 1はすべて同じく値を実数型から整数型へと変換できる) FLOOR(A)とA>>0はAの値が正数であれば両者は全く同一と思われがちですが、同じ整数値であってもFLOOR(A)は実数型、A>>0は整数型であるため覚えておくとよいかもしれません。
0そうだね
プレイ済み
返信[8]
親投稿
おちゃめ ochame_nako
もちろん、こんな知識がなくても上記の「0.0ではなく0を代入したら整数型になる」ということさえ知っていれば下記のように記述することもできます。 DEF X2 A OUT B  B=0  B=A  IF A>0 THEN B=B*1.5 END Bに0を代入した時点でBは整数型の変数になり、それで型が決定されるためB=AとしてAを代入したらBにはAを整数値に丸めた値が入り、B=B*1.5も整数値に丸めらた値が変数Bに入ります。
0そうだね
プレイ済み
返信[9]
親投稿
おちゃめ ochame_nako
プチコン3号の自作関数の引数の変数の型、OUT以降の戻り値の変数の型は最初に値を代入した時点で型が決まるというのは最初に書いた通りであり、型を曖昧にせずサフィックス(#や%や$という記号)通りにきちんと動作するようにすれば扱いやすくなりそうですが、その場合はできることが減ってしまいます。(型が厳格化された場合には整数型、実数型、文字列型すべてに対応させた関数を作ることはできず、3つの関数を作りそれを使い分ける必要性が出てくるため不便になる)
0そうだね
プレイ済み
返信[10]
親投稿
おちゃめ ochame_nako
長くなりましたが、DEFで自作関数を作る場合には変数の「型」についてよく理解しておく必要があります。それはDEFで挙動不審になる場合は多くの場合はこのようにDEF内では代入された時点で型が決まるため想定した動作にならない場合があるためです。 原因を知らなければ「単なる原因不明の誤動作扱い」となってしまいますが、どのような場合があり、どのように対策すれば良いかを知っておけば難しいことは何もありません。 今回書いたことが皆さんの役に立てればと願っています。
0そうだね
プレイ済み