データ型とデータ入力基礎
これまでは自分でクラスを設計し、実物を作ってきました。 しかし、吉里吉里/TJSには設計図(クラス)が予め用意されていて、実物を作るだけで使用できるものがいくつかあります。 では、新しい機能の紹介に入る前に、Systemクラスのメンバー関数をもう一つ紹介しておきます。
ユーザ入力関数
新たに使うメンバー関数の名前はinputStringです。 この関数は、文字の入力を促すウィンドウを表示し、ユーザーから文字入力を受け付けることが出来る関数です。
使い方の例をご覧ください
var str = System.inputString("入力画面","何か入力して下さい","");
System.inform( "入力文字:"+str );
入力した文字を変数strに代入し、これを表示させています。 入力した文字がちゃんと表示された事を確認してください。 つまりメンバー関数inputStringは、入力された文字を返す処理を行う事が分かります。
指定している引数について説明を入れておきます。
inputString( 題名 ,入力を促す文字 , 初期値)
- 題名
- 表示するウィンドウの題名です。例えば「文字の入力」などと書きます。
- 入力を促す文字
- 何を入力すれば良いか分からないので、簡潔に説明文を入れます。 例えば「文字を入力してください」などと書きます。
- 初期値
- 最初に入れておく値を指定します。 例えば数字の1を書いておくと、 ウィンドウが表示された時点で1が既に入っている状態になります。 特に指定しない場合は「""」などと、空の文字を指定しておきます。
この中で題名と入力を促す文字は、文字列を指定する必要があるので、 必ず""で囲む事を忘れないようにしてください。
各引数は値を省略する事も出来ます。 値を省略するというのは、引数は与えるが値を与えないという書き方です。
inputString( ,, )
これは各引数に値を与えてはいませんが、 引数は渡しています。 この時渡されるのは何も無い引数、つまりvoidです。
数字と文字の違い
System.inputString関数には欠点があって、数字の入力を受け付ける事が出来ないのです。 最初のプログラムで「1」と入力したら、ちゃんと1と表示はされます。 ただし、それは文字としての1なのです。それを確かめましょう。
var num = System.inputString("入力画面","数字を入力して下さい","");
System.inform( "1+入力数字="+(1+num) );
1と入力数字の足し算をして表示する、というプログラムなのですが、 100と入力しても1100と表示されるし、1と入力すると11と表示されます。 これは「数字の1と文字列の足し算」を行っているからなのです。 (以前に「こんにちは」と「数字の1」を加算すると「こんにちは1」という文字列になるという話をしましたが、同じ仕組みです。)
よって、このプログラムには、以下の改良点がある事になります。
- 入力された値が数字にはならないので、数字に変換しなければならない
- 入力する値が、そもそも文字列の可能性があるので、 数字以外が入力されると再入力を促す必要がある
二つ目の問題について、入力する値が文字列にならないよう気を付ければ良いじゃないか、 と思われるかもしれませんが、そうもいかないのです。
普通、プログラムは自分で利用する為に作る事は多くありません。 ゲームを作って自分だけで楽しむという事はまずしませんよね?折角苦労して作ったのだから、他人に遊んでもらいたいもの。
つまり、プログラムを作る場合、内部仕様を知らない事を前提として作っていかないと、 時にはとんでもない問題が発生する事がある、という点を頭に入れておかなくてはなりません。
今回の問題を解決するには、入力された値が正しいかを確認する必要があります。 自分(プログラム作者)が入力するのなら、数字以外を入力しないように気を付ければ良いのですが、 入力するのが他人であるなら、ちゃんと数字を入れてくれるという確証は無いのですから。
データ型
遠回りしましたが、実はTJSの本質はここから始まります。 「データ型」とは、他プログラム言語なら真っ先に説明しなければならないほど、とても重要な要素なんです。 何故ここまで引き伸ばしてきたか…それは、TJSが自動的にデータ型を判断して処理してくれるからです。
これまで、変数には様々な値を入れてきました。 0や10などの数字、"こんにちは"などの文字列。 また、クラスの実物(オブジェクト)も入れることが出来ましたし、 あるオブジェクトのメンバー関数の別名としても利用できました。
このように、TJSでは変数に何でも入れる事が出来、自由に使う事が出来るのですが、 吉里吉里の内部では、これらの変数に何が入っているのか? 言い換えるなら、その変数は何を指しているのかを明確に区別しています。 (これまで、全て変数と呼んできましたが、内部的には全く異なるものなんです。)
- num = 1 ……数値の1
- str = "OK" ……文字列のOK
- obj = new myclass() ……myclassのオブジェクト
このような値の種類をデータ型と呼びます。 TJSではある値を入れる時や演算する時などに、その値が何なのかを判断し、変数のデータ型を自動的に設定してくれています。 だから、データ型を気にする事も無く、変数を利用出来たのです。
データ型の余談
TJSやJavaScriptといったスクリプト言語を使っていると、これは当然のように思われますが、 多くのプログラム言語では、このような型の自動設定は行ってくれません。 なので、文字列と数値の加算なんて処理は絶対不可能なんです。
よって「数値を入れる変数」「文字を入れる変数」 などと入れる値に応じて、専用の変数を用意する必要があるんですね。 面倒くさい…と思うかもしれませんが、プログラムに慣れてくると、この方が変数の管理が格段にやりやすいメリットもあります。
データ型の種類
TJSではデータ型を自動的に設定します。そのおかげで変数を自由に扱えます。 その代償として、その変数がどういう型なのかを見分ける事が出来ません。
そこで、変数のデータ型を知る為の予約語として、 typeofという単語が用意されています。
System.inform( typeof "こんにちは!");
これを実行すると「String」という文字列が表示されたと思います。 Stringとは文字列型を示す文字列です。 「こんにちは!」は文字列ですから、Stringを返してくれたという事になります。
単語typeofに続いて、変数(または数値や文字列そのものでも可)を書き込むと、
その変数のデータ型を示す文字列を返してくれる、というわけです。
以下にデータ型の例を示すと共に、簡単な解説を行います。
※TJS2リファレンスの順番に準じます。
データ型の名称 ( 記号typeofが返す文字列 )
- void型 ( void )
voidとは何も表していない事を指す特別な文字です。 空っぽの変数という意味でもなく、本当に何も指しません。 例えば次のような例です。
var i="";
これは何も入っていないように見えますが、 空の文字列が入っています。つまり変数iは文字列型です。
var i;
英文字iを変数として使う事を示しただけですので、 値は何も表していません。つまり変数iはvoid型です。
尚、void型の変数自体は偽(つまり0)を返します。 これはif文などで、void型の変数を利用する際に覚えておくと便利です。
(ただし、一つ注意点があります)- 整数型 ( Integer )
-
整数は簡単に言えば数字です。 例えば、「1」「100」「88」などは整数型です。 これは正と負を問いません。ですから
「100」「-800」「-9999」
これらは全て整数型です。
- 実数型 ( Real )
-
実数というのは、より正確な数字というような意味合いです。 簡単に言うなら小数点の付いた数字です。 TJSでは普通の数字と小数点付きの数字を分けて処理します。
「0.01」「20.0」「99.9999」
これらは全て実数型です。 (20.0は20と同じですが、小数点を付けると実数になります。)
- 文字列型 ( String )
-
これは字の通り、文字列を指します。 「おはよう」「こんにちは」などが文字列です。
もっと簡単に言うなら「全角文字」が文字列になります。よって
「全角スペース( )」「@」「9999」
これらは全て文字列です。
- オブジェクト型 ( Object )
これはクラスでも説明したとおり、実物そのものを指します。 クラス自身や関数も実態がありますので、オブジェクトを指します。
function func() { } System.inform( typeof func );
これを実行するとObjectと表示される事を確認してください。
- オクテット列型 ( Octet )
これを書いている現在、 オクテット型は完全に確立されていないようなので、 説明は割愛させて頂きます。
C/C++
各データ型は次のように置き換えて考えてください。
整数型 …… int、long
文字型 …… char
実数型 …… float、double
void型 …… void
voidの注意点
void型を扱うには注意すべき点があるので紹介しておきます。
変数numにある数値が入るとします。 ここで、数値に何か入っていれば真を指すtrueを代入、 入っていなければ偽を指すfalseを代入したいとします。
これはif文で処理を分岐させる事が出来ますね。 変数に何も入っていないという事は、その変数はvoid型であると言えます。 void型の変数は偽を返すのだから……と言って、次のような分岐をさせるのはいけません。
if ( !num ) { num = true; } else { num = false; }
記号!は真偽を逆転させる記号でしたよね。 よって、変数numが偽ならば処理をする、というif文になっています。
しかしながら、これは間違いです。なぜなら、偽とは数値の0だからです。 変数numに0が入っていた場合、0という数字が入っているにもかかわらず、何も入っていないと判断されてしまう。
ここではvoid型を表す単語voidと変数を比較するのが正解です。例えば次のように。
if ( num == void ) { num = false; } else { num = true; }
ただし、==についてリファレンスには次のように書かれています。
両辺の演算の結果、型が異なれば適切に変換されてから比較されます。 たとえば、-1 == '-1' は真になります。
ここでは、変数numがvoid型である事を示したいのですから、型が違った場合に勝手に変換されたら困ります。 そこで、新しい記号===を使います。(イコールを三つ使います) ===は、左右の値とデータ型が完全に一致しないと真になりません。よって…
-1 === '-1'
これは整数型と文字列型との比較なので、偽になります。 今回はこの===を使うのが正解です。 では、プログラムを仕上げてみましょう。
var num; if ( num === void ) { //値が入ってなければfalseを代入 num=false; System.inform( "変数numには何も入っていません" ); } else { //値を表示 System.inform( "numの値は"+num+"です。" ); }
また、==の時と同じく、真偽を逆転させることも出来ます。 これは「!==」という記号を使います。
if ( num !== void )
この例では、変数numがvoidで無ければ、というif文に変わります。
改良プログラム構想
さて、少々寄り道しましたが、これまでの知識を使って、 入力された数値同士を計算させるプログラムを改良していきましょう。 まずは、どのような問題点があって、どのように改良するのかをまとめます。
入力値を整数にする
関数inputStringは文字列しか返してくれませんので、次の方法で整数型へ変換します。
int 変数
int という予約語に続いて変数を書けば、右側の変数を整数型に変換して返してくれます。 整数型になりますので実数は扱えません。 (intは小数点を全て切り捨てますので、0.99999...は0になります。四捨五入では無い点に注意してください。)
実数を扱いたい場合は、次の方法を使います。
変数 - 0
ある値から0を引けば、数値に変換されます。 intとの違いは整数型に変換するわけではない点です。 入力値が10の場合は10、1.5の場合は1.5という数値に変換されます。
実数値へ変換するには、次のようにしても行えます。
real 変数値
※intやrealは「式」として整数や実数を返すだけです。 ですから、関数へ渡したり変数へ代入したりしなければ、右側に書いた変数に何の影響もありません。
では、次のプログラムを実行して、実際にどういう値になるのか確認して見てください。 特にint変換と減算による変換の違いをご覧ください。
var num = System.inputString("入力画面","数字を入力して下さい",""); var n1 = int num; var n2 = num - 0; System.inform( "純粋入力値 :"+num+"\t「"+(typeof num)+"」\n"+ "整数変換値1:"+n1+"\t「"+(typeof n1)+"」\n"+ "実数変換値2:"+n2+"\t「"+(typeof n2)+"」" );
入力値を繰り返し入力させる
まだ説明していなかった事ですが、プログラムでは数値を0で割る事は出来ません。 例えば次のような文は間違いです。
var sum = 7899 % 0;
よって、0が入力されたり、 文字が入力された後で整数型に変換され「0」になったりすると、この問題が発生します。 よって、数値として0を返されると再度入力を求める必要がありますので、 繰り返し処理を使います。
値を入力→値が0か?→0なら再度入力……
このような反復処理を行わせるのに最適なのはdo~while文です。 この文は、最低でも一度は処理を行うものでしたね。 値を入力するという行為は最低でも一度は行わせる必要があります。 こんな時、do~whileを使うほうが便利なのです。
処理を関数にする
数値を入力するという処理は、後から何度でも行えれば便利です。 よって、この一連の処理を関数にまとめ、後からこの関数を呼び出すだけで同じ処理が行えるようにします。
こうして作った関数は「プログラムの部品」としての価値がありますから、 別のプログラムで「標準入力関数」としての使い道が生まれます。
これで構想は終わりです。次回から早速実装してみることにします。
構想を怠るべからず
プログラムを作る場合、事前に処理の流れを考えておく事はとても大切です。 何も考えずに作ってしまうと、後で大きな変更を強いられ、 時には一から作り直したほうが早い、なんて状況も生まれます。
ですから、作りたいプログラムがあれば、メモ帳でもいいので、処理の流れを出来るだけ細かく書き込んでいく。 それを頭から見直して変だな?と思う処理は随時訂正していく。 上から順番に処理を行う特性「連接」という考え方に基づいてプログラムする場合、特に重要な作業です。
面倒かもしれませんが、後でプログラムを修正するより、よっぽど短時間で終わりますから、 結果的に早くて正確なプログラムが書けることになります。
標準入力関数の実装
ユーザからの入力値を整数に変換し、値が0ならば再度入力を求める事にします。
function getInt(cap="整数入力", prom, str) { var num; do { num = int System.inputString(cap , prom , str); } // numが偽(0)なら再度入力 while( !num ); return num; }
※引数 cap , prom , strは、それぞれタイトルバーの文字、入力を促す文字、初期値の順です。
※whileの条件は num == 0 としても構いません。
この関数は今後も使うことを考えて、別ファイルで保存しておきましょう。 今回は「 userInput.tjs 」という名前で保存します。(好きな名前で構いません) そして、使う場合は入門の最初に紹介した関数「Scripts.execStorage()」を呼びます。
Scripts.execStorage("userInput.tjs");
これをstartup.tjsの先頭に書き込めば、整数入力関数getInt()を使えるようになります。
この関数では整数の0を利用する事が出来ません。 より汎用的に改良するにはどうすれば良いのか?考えてみてください。
やり方はいくつかあります。関数自体を修正する。 関数を使う側で、特定数値が入力されたら0とみなす……。
四則演算の仕上げ
最後に、数値を二つ取得し、 「足し算」「引き算」「割り算」「掛け算」という四則演算をさせ、 これを表示できれば完成としましょう。 その前に、危惧する点があるので、これを説明しておきます。
整数の限界
ユーザーは、時にプログラマーの予想を大きく裏切ります。 整数を入力してくださいって書いてるのに「あ」と入力したり、 9999999999999...なんてありえないくらい大きな数字を入力したりする。
実はTJSには計算出来る数字の大きさには限界があり、それを超える事は出来なくなっています。 よって、大きな数値を掛け算に使うと、この限界を超える事が考えられます。
ここでは、演算前に次のような三項演算子を使うと解決出来ます。
num1 = ( num1 > 9999 ) ? 9999 : num1;
数値が9999を超えていれば9999とする。それ以外は、入力値をそのまま使用する。 これで最大でも9999×9999までの計算に留まります。
※分かりづらければif文を使っても構いません。
では、入力した二つの値の四則演算プログラムを完成させます。
Scripts.execStorage("userInput.tjs"); System.inform("四則演算を行います。数値を二つ入力してください。\n※ただし、0は入力出来ません。"); var num1 = getInt("数値1の入力"); num1 = ( num1 > 9999 ) ? 9999 : num1; var num1 = getInt("数値2の入力"); num2 = ( num2 > 9999 ) ? 9999 : num2; System.inform( num1+"+"+num2+"="+(num1+num2)+"\n"+ num1+"-"+num2+"="+(num1-num2)+"\n"+ num1+"×"+num2+"="+(num1*num2)+"\n"+ num1+"÷"+num2+"="+(num1/num2) );