GO BACK

NewtonScript教室 その6

「おいしいスープを調理しましょう!」



■スープは、さまざまな「具」のごった煮です

 Newtonは、いわゆる「情報機器」です。これでプログラムを組むといっても、あんまり「DTPソフトを作ろう」とか「3Dグラフィックツールを作ろう」と思う人はいないでしょう。Newtonが自力を発揮するのは、住所や電話番号、予定やメモなどをごったに入れておき、それを必要に応じてパッと呼び出す−−そういう時です。ですから、Newtonでプログラムを作る時も、やぱりそうしたデータを検索したり表示したりするようなものが中心となることでしょう。

 では、「Names」や「Dates」で入力したデータを自分のプログラムから呼び出すことは可能なんでしょうか? その答えは「YES」です。というより、それこそ、Newtonでの開発の神髄ともいうべき部分なのです。

 普通、コンピュータのアプリケーションで作成したデータを自作のプログラムから呼び出す時は、かなり大変な作業になります。まず、たいていのデータはファイルの形で保存されています。ディスクのどこに必要なファイルがあるのかを調べないといけません。そしてファイルの内部構造を調べ、どこにどんなデータが書き込んであり、それをどうやって調べて取り出すか、全て知らねばなりません。

 ところがNewtonの場合、他のプログラムで入力したデータを取り出すのはとても簡単なのです。その理由は、Newton特有のデータ構造になります。Newtonでは、いわゆるファイルにデータを書き込んで保存するというパソコンの基本ともいうべき方式は通用しないのです。

 では、どのような形で記録されているのか。−−それは「スープ」というものです。

 スープというのは、Newton特有の特別なメモリ空間、とでも定義できるでしょうか。要するに「データを保存する専用のメモリ場所」と考えて下さい。さまざまな情報を記録しておく必要があるとき、Newtonではこの「スープ」にデータを書き込みます。

 スープには、全てのデータが放り込まれます。「Names」で書いた名前と電話番号、「Dates」に記入した来週のスケジュール、「iobox」で送信したメールの情報、ありとあらゆるデータがスープの中に放り込まれています。いわば、スープは「データのごった煮」なのです。

 スープのエリアの中には、それぞれのデータが種類ごとにパックされてぷかぷか浮かんでいます。「Names」で入力したデータ、「Datas」でのデータ、といった具合ですね。そしてそこから何かのデータを取り出したいと思ったら、取り出す条件を設定してスープに要求を出します。するとNewtonは指定された種類のスープから、条件に見合うものをピックアップして渡してくれるのです。

 ユーザーは、スープの内部がどのような構造になっているか知る必要はありません。何番目に何のデータがあるかなんてどーでもいいのです。「こういう条件のものを探して」といえさえすれば、いつでもスープから条件に見合ったものを渡してくれるのです。なかなか便利そうなシステムでしょ?



■「Names」スープから名前を取り出す

 では、さっそくスープを調理してみることにしましょう。例として、「Names」スープから名前の読みがなを検索して取り出してみましょう。

 前回のプロジェクトを開いて下さい。確か、InputData・OutputData・ClickMeという3つのビューがAppビューの上に配置されていましたね。この中から、OutputDataを削除して下さい。そして、ツールパレットから「protoInputLine」テンプレートを選択して新たにビューをAppの上に配置します。

 このビューは、「Templete Info」メニューを選んで名前を「OutputData」と設定しておきます。もちろん、「Declear to」ボタンをチェックして、Appから見えるようにして下さい。

 というわけで、修正が終わったレイアウトはこんな感じになります。

 なぜ、せっかく作ってあったOutputDataを消して、わざわざ新しくprotoInputLineで作り直したのか? それは、先に作ったOutputDataは、protoStaticTextテンプレートを使っていたため、たくさんの行のデータを書き出すのに不向きだからです。それに、検索されたテキストを選択して再利用できたほうが実用的ですからね。そこで、テキストを選択できてしかも複数行が表示できるprotoInputLineテンプレートに変えた、というわけです。

 …実をいうと、こんな面倒なことをしなくても、ビューの_protoというアトリビュートを変更すれば、テンプレートの種類をぱっと変えられるんですが、一度削除して作り直したほうが、何をやっているかわかりやすいのでそうしてみました。興味のある人は、_protoを変更するとどうなるか試してみて下さいね。

 さて、レイアウトができたところで、今度はプログラムの方へ進みましょう。まず、ClickMeビューのbuttonClickScriptを以下のように書き換えます。

func()
begin
	local str,str2,mysoup,mydata,position;
	str := InputData.text;
	str2 := nil;
	mysoup := getUnionSoup("Names");
	mydata := mysoup:Query({text:str});
	position := mydata:entry();
	for i := 1 to 5 do
	begin
		if position = nil then break;
		str2 := str2 & $\n & position.sorton;
		position := mydata:next();
	end;	
	SetValue(OutputData,'text,str2);
end

 ちょっとなんだかわからないところがあるでしょうが、説明はあとでしますので、とにかくプログラムを完成させてしまいましょう。−−次に、InputDataのviewClickScriptです。こちらは、確か「クリックすると数字キーボードが現れる」というのを組み込んでありましたね。これを、以下のように変更して「ローマ字インラインキーボードが現れる」というように変えておきましょう。

func(unit)
begin
	// Return true if click has been completely handled, nil otherwise
	GetRoot().|RomajiIn:ENFOUR|:open();
	return nil;
end

 これで完成です。−−できたらコンパイルし、Newtonに転送して動かしてみましょう。InputDataに適当に文字を入力してボタンをタップすると、その文字を含む「Names」の「読み」が一覧で表示されますよ。

 では、プログラムを見てみましょう。今回のプログラムでは新しい命令がたくさん出てきますから、1行ずつ理解していく必要があります。


local str,str2,mysoup,mydata,position;
まずは変数の定義からです。今回はいろいろなデータを変数におさめるので、最初に各変数の役割を知っておきましょう。それぞれには、以下のようなものを入れる予定です。

	str	→	InputDataのテキスト
	str2	→	スープから取り出した「読み」のテキスト
	mysoup	→	「Names」スープ
	mydata	→	スープから検索したデータ
	position	→	データ内で現在設定されている項目

 ちょっとわかりにくいかも知れませんが、スープからデータを取り出す時は、「スープ」「スープから探し出したデータ」「データの現在示されている項目」という3つの要素が必要になる、ということを頭に入れておいて下さい。


str := InputData.text;
str2 := nil;
これは問題ありませんね。InputDataのテキストを変数strに代入しています。またstr2にはnilを代入しておきます。nilというのは、「何もない状態」でしたね。


mysoup := getUnionSoup("Names");
ここで、スープを変数mysoupに代入しています。「getUnionSoup()」というのは、指定した名前のユニオンスープを返す関数です。

 ユニオンスープというのは、あちこちに置かれているスープのデータを全て1まとめに扱えるスープの事です。例えば、Newtonにメモリカードを差していたとしましょう。すると、「Names」のデータは、内蔵メモリとメモリカードの2つの場所にばらばらに置かれる可能性も出てくるわけです。これを、いちいち両方調べるのはちょっと面倒ですね。

 そこでユニオンスープの出番となるわけです。ユニオンスープは、調べるスープの名前だけ指定すれば、そのスープがどこに保存されていても関係なく全てをまとめて扱うことができます。ですから、特に理由がない限り、「スープを扱う時はユニオンスープを使う」と覚えてしまいましょう。

 もちろん、ユニオンスープという名前は、「オニオンスープ」のしゃれでつけられたものだ、と筆者は硬く信じています(笑)。


mydata := mysoup:Query({text:str});
さて、スープを指定したら、このスープから必要な条件に見合ったものだけを取り出す作業になります。これをやってくれているのが「Query()」という関数です。これは、パラメータに指定した条件に見合う項目をスープからピックアップしてまとめて返すものです。この関数は、ちょっと書き方がわかりにくいですね。

《スープ》:Query ( { 《条件1》:《値》,《条件2》:《値》,…} );

 このように書くと少しはわかりやすくなったでしょうか。パラメータには、検索の条件が指定されるのです。この検索条件は、1つの場合もあるし、2つ以上もある場合だってあるでしょう。そこで、このように両側を{}記号でくくって、その中に条件をカンマで区切って並べて書くようにしてあるわけです。

 今回指定した条件部分を見ると、このようになっています。

 {text:str}

 条件の「text」というのは「指定したテキストを持っているものを検索する」という時に使うものです。つまりここでは、変数strというテキストを含む項目を検索していたのですね。

 検索条件にはtext以外にもあるのですが、とりあえず今回は他にも新しい命令がたくさんあるので、このtextだけしっかり覚えておくことにしましょう。


position := mydata:entry();
さて、前の命令で、変数mydataに検索された全データが収められました。この中には、たくさんの項目が入っているはずです。この1つ1つの項目の事を「エントリー」といいます。検索されたデータの中には、たくさんのエントリーが入っている、というわけですね。

 このエントリーは、どこにあるものでも適当に引っ張ってこれるというわけではありません。「現在はこのエントリーを使う」ということが決まっていて、そのエントリーの値だけを取り出すことができるのです。わかりやすく「カレントエントリー」とでも呼ぶことにしましょう。

 このカレントエントリーを返す関数が「entry()」というものです。ここでは「mydata:entry()」と実行していますね。こうすると、mydataのカレントエントリーが返されるというわけです。

 たくさんのエントリーが入っている場合は、カレントエントリーから必要な項目を取り出し、それがすんだらカレントエントリーを次のエントリーに移動してまた情報を取り出し、また次のエントリーに…というように、カレントエントリーを順番に移動させながらデータを取り出していくのです。


for i := 1 to 5 do
さて、ここから繰り返しになります。繰り返しを使って、データを取り出してはカレントエントリーを移動する、ということをやるわけですね。ここではとりあえず5回繰り返しています。つまり、検索されたデータの始めの5つを取り出そう、というわけです。


if position = nil then break;
ここでの「break」というのは、繰り返しから抜け出る命令です。positionがnilであった場合には、そこで繰り返しを中止し、抜け出すようにしてあるわけです。

 条件となっている「position = nil」というのはどういう意味でしょうか? positionというのは、mydataのカレントエントリーでしたね。ということは…そう、エントリーがない場合の処理をしていることになります。繰り返しは5回あるけれど、エントリーが3つしかない、という場合は、4つめエントリーにカレントエントリーが移動すると値はnilとなってしまうのです。だってエントリーがないのだから、考えてみえば当たり前ですね。


str2 := str2 & $\n & position.sorton;
ここで、変数str2に、positionのsortonというスロットの値をつえ加えています。sortonというのはソートの基準となる項目の事で、Namesスープの場合は「読みがな」が指定されています。つまり「position.sorton」で、カレントエントリーのソート項目の値を取り出しているわけですね。

 その前に、なんだか変な記号があります。「$\n」というものです。これは、実は改行記号を意味するものなのです。2番目のキャラクタが文字化けをしているので注意して下さい。これは、日本語フォントでは¥記号、英語フォントでは\記号(バックスラッシュ)になります。つまり「str2 & $\n & position.sorton」というのは、str2の後に改行してposition.sortonの値をつなげていたわけです。


position := mydata:next();
これは、カレントエントリーを1つ移動しているところです。「next()」というのは、カレントエントリーを1つ先へ進めるものです。これで、次のエントリーをカレントにしてpositionに設定しているのですね。


SetValue(OutputData,'text,str2);
繰り返しを抜け、最後にSetValueでテキストをOutputDataのtextに設定すれば終わりです。



■スープのデータを知る

 なかなかややこしそうですが、手順さえしっかりと覚えてしまえば、スープを利用するのは決して難しくはありません。ざっと使い方をまとめるなら、以下のようになるでしょう。


1.getUnionSoup()で指定のユニオンスープを取り出す。
2.Query()で指定した条件にあうエントリーをまとめて取り出す。
3.Entry()でカレントエントリーを取り出す。
4.カレントエントリーのスロットを調べて必要な値を得る。
5.値の取り出しが終わったら、next()で次のエントリーに移動し、4に戻る。


 スープから取り出したエントリーをどう利用するかわかれば、スープはけっこう簡単に使えるようになります。問題は、スープにどんなスロットがあるのかでしょう。これがわからないとデータが取り出せませんね。

 実をいえば、スープのアクセス方法より、こっちのほうが面倒臭いのです(笑)。例えば、「Names」にはたくさんの項目がありますね。それらが全てスープの中に入っています。これを1つ1つ取り出すためには、必要なスロットを全て調べないといけません。

 では、「Names」スープの中の主だったスロットを説明しておきましょう。


name.first (姓)
name.last (名)
これが姓と名前のスロットです。名前は、「読み」「姓」「名」の3つが「name」というものにまとめられており、そこにそれぞれ「first」「last」というスロットが用意されているのです。ですから、実際に取り出す時は、「《カレントエントリー》.name.first」というように指定してやらないといけません。「《カレントエントリー》.first」ではうまく取り出せないので注意が必要です。


address (住所1行目)
address2 (住所2行目)
city (市/区)
resion (都道府県)
country (国名)
住所も取り出すのは大変です。こちらは「国名」「都道府県」「市/区」「住所(2行ある)」という形で入力していますが、それらが全てバラバラに入っているのです。ですから、住所を調べる時は、これらを全て調べ、1つにつなげてやらないといけないのです。


phones
電話も大変です。なぜなら、電話は「phones」だけでは取り出せないからです。電話番号は複数のデータを1つにまとめてあるため、「配列」というものを使って整理しています。配列というのは、複数の値を1つにまとめたものです。

 配列は、変数名の後に[番号]というものをつけてやらないといけません。番号は、0番が1番最初になります。ですから、「電話番号の1番目を取り出したい」というときには、「《カレントエントリー》.phones[0]」という具合になるのです。ちょっと複雑ですね。


email
これは単純ですね。電子メールはそのまま、emailで取り出せます。


 とりあえず、これで「Names」スープの主なスロットは取り出せるようになるはずです。先に作ったプログラムを作り替えて、他のスロットからスープの情報を取り出してみて下さい。スープはけっこう面倒だけど、やり方さえ覚えてしまえば、どんな情報でも同じように取り出せることがきっとわかるでしょう。

 それでは、先ほどのプログラムを改良して、漢字表記の氏名と電話番号を書き出すようにしてみましょう。

func()
begin
	local str,str2,mysoup,mydata,position;
	str := InputData.text;
	str2 := nil;
	mysoup := getUnionSoup("Names");
	mydata := mysoup:Query({text:str});
	position := mydata:entry();
	for i := 1 to 5 do
	begin
		if position = nil then break;
		str2 := str2 & $\n & position.name.last & position.name.first & ~
			$\n & position.phones[0];	←※1行に続けて書いて下さい
		position := mydata:next();
	end;	
	SetValue(OutputData,'text,str2);
end

 ボタンのbuttonClickScriptを以上のように書き換えれば完成です。書き換えたのは、※の行だけです。これは長いので改行してありますが、〜を消して、1行につなげて書いて下さい。

 これを実行すると、検索文字列を含むカードの氏名と電話番号を抜き出します。ちょっと実用的になってきましたね? 他にもどんなことに利用できるか、それぞれで試してみて下さい。


GO NEXT


GO HOME