ハイパーカードの使い方ヒント

「ハイパーカードは遅い」と思われがちですが、工夫次第では十分快適なスタックを作ることができます。スムーズな動作やスピードアップのためのヒントをいくつか紹介します。とりあえず、広く知られている内容が中心ですが、この先、本人の体験に基づいたヒントも加えて行く予定。参考になれば幸いです。

ディスクへのアクセスをできるだけ少なくする

ご存知の通り、メモリに比べてディスクへのアクセスは格段に時間がかかります。ハイパーカードには「保存」メニューがありませんが、その代わりに様々なタイミングで情報を自動的にディスクに保存しています。また、スタックのデータも一度にメモリに読み込まれるのではなく、必要に応じてディスクを参照することがあります。

こうしたディスクアクセスは、スクリプトを工夫することでかなり減らすことが可能です。その結果、実行速度の向上が期待できます。

製品版のマニュアルには具体例として次のような点があげられています:

演算にはフィールドではなく変数を使う

フィールドに書き込むとディスクアクセスが発生します。これを避けるために、スクリプト中でデータを一時的に保持するときはフィールドでなく変数を使うようにするべきです。

フィールドを使うと時間がかかるのは、ディスクだけでなく画面の描き換えも関係しています。表示されているフィールドに次々にデータを書き込むと当然画面上でもデータを表示しなければなりませんから、余計に時間がかかることになります。逆に言えば、フィールドでも隠された(hideされた)フィールドを使うと、比較的高速になります。

例えば

put "Sun,Mon,Tue,Wed,Thu,Fri,Sat" into str
repeat for 50 times
  put item (the random of 7) of str into container
end repeat

のようなコードの場合、containerが変数ならば約10 ticks、隠されたフィールドなら約20 ticksであるのに対し、画面上で見えているフィールドに書き込むと約150 ticksと、段違いに時間がかかることが分かります。

(1 tickは1/60秒)

さらに、フィールドに保存する場合、数字はいったん文字列に変換されますが、変数ではバイナリ(数字)として扱われますので、この分も大きな差となって表れます。

repeat for 50 times
  put the random of 10000 into container
  put container / 2 into container
end repeat

のような場合は、変数が5〜6 ticks、隠されたフィールドなら約45 ticks、見えているフィールドに書き込むと約340 ticksと、一段とスピードの差が大きくなっています。

スタックの変更(移動)を最小限にする

一般にスタックを変更(移動)するということは、ディスクの参照や書き込みというアクセスが生じるので、頻繁なスタックの切り替えはスピードの低下を招きます。

別のスタックにある情報を取得する必要がある場合は、何度にも分けて情報を取り出すのではなく、1度の移動で必要な情報をまとめて取得するようにします。また、情報を異なる切り口で提示する場合は、複数のスタックを使うのではなく、必要なバックグラウンドを作成して、同じスタック内で情報を表現するようにします。単に情報を保存するだけならば、スタックを使わずにテキストファイルを使うのも良いでしょう。

カードを移動するのでなく、参照する

離れたカードのフィールドに収められているデータが必要な場合、一般にgo命令でカードを移動するよりも

get field 1 of card x

のように、参照する方が高速です。しかし、同じカードの複数のフィールドから情報を取得する場合、例えば10のフィールドのデータが必要となるような時には、カードを移動してからまとめてデータを取得する方が高速になります。Apple社の「スクリプトランゲージガイド」では、フィールドの数が10を越えるときは移動してデータを収集することを勧めています。

このようにどうしても移動が必要な場合、特にユーザーに移動先の画面を見せる必要がなければ画面描き替えをロックするとスピードを向上させることができます。

スピードを上げるスクリプトの書法−1

ディスクアクセス以外にもスクリプトの書き方で実行速度を上げる方法がいくつかあります。これらは1つ1つはそれほど劇的な効果を持つわけではありませんが、これらの積み重ねがスクリプト全体としての効率を高めてくれます。

変数名を短くする

妙な話ですが、変数の名前を短くすると、実行速度が上がります。ハイパーカードがメモリー内部で変数を参照する効率が良くなるようです。ただし、これは後で述べる読みやすいスクリプトとのトレードオフになるので、ほどほどにするのがよいでしょう。

文字列は引用符(")でくくる

スクリプト内で文字列をそのまま使うときは、引用符でくくって変数と区別させます。変数と同じ名前でない限り引用符なしでもエラーにはなりませんが、変数と区別することでハイパーカードがいちいち変数テーブルを調べる必要がなくなります。

これはanswerなどで表示させる文字列だけでなく、ボタンやフィールドの名前も同じことです。例えば、「MyText」という名前のフィールドの中身を取得するとき

get field MyText

とすると、ハイパーカードはMyTextという変数にフィールドの名前が納められていないかどうか確かめてからMyTextという名前のフィールドにアクセスすることになります。ここは

get field "MyText"

とすることで、ハイパーカードは最初から正しいフィールドにアクセスできるわけです。

関数の呼び出しはthe xxxxの形式を使う

ハイパートークの組み込み関数の呼び出しはtheを使う方法と()を使う方法の2通りがあります。たとえば、今日の日付を調べるにはthe dateとしてもdate()としても同じ結果が得られます。

このとき、the dateとするとこの呼び出しは直接ハイパーカードに送られますが、date()とした呼び出しは全てのメッセージの階層を通って、同じ名前で再定義された関数がないかどうかチェックした上でハイパーカードに送られることになります。

複数のメッセージをまとめる

簡単な話としては

get x
put it into y

とするよりも

put x into y

のほうがよいということです。もう少し高度な話としては、演算子is in, containsのほうがoffset()を使うよりも高速になるという例が挙げられます。これは前述のtheを使った関数呼び出しと同じく、演算子は直接翻訳されてハイパーカードに送られますが、offset()はメッセージとして伝達されてしまうからです。

同じ(=)かどうかの比較より同じでないか(<>)の比較が速い

これも不思議な感じですが、ハイパートークをコンパイルしたときの機械語の関係でしょうか、=を使った比較よりも<>の方が速く実行されます。例えば次のループ文で

repeat until i=10000
  (do something)
  add 1 to i
end repeat

とするよりも

repeat while i<>10000
  (do something)
  add 1 to i
end repeat

のほうが実行時間が短くて済むのです。

変数の参照を少なくする

具体的にはこういうことです。変数xをyだけ増加させようという場合、

put x + y into x -- (1)

とする方法と

add y to x -- (2)

とする方法があります。このとき、(1)のやり方ではxを2回とyを1回、計3回の変数参照がありますが、(2)ならばx, yともに1回ずつの2回の参照で済んでいます。従って(2)の方が高速なスクリプトになるというわけです。

スピードを上げるスクリプトの書法−2

コードの書き方として、ハイパートークに限らない一般的な注意があります。

ループの中に不要なコードを置かない

当然のことですが、ループの中のコードは何度も繰り返し実行されます。ですから、1度行えば十分な命令は、必ずループの外側に置くようにします。例えば

repeat with i=1 to 100
  put 1 into x -- (1)
  put x + i into y
  ...
end repeat

のようなコードでは、(1)の部分はrepeatの前に置くべきです。

ハンドラ呼び出しより実行文を並べる方が速い

長いスクリプトの場合、一部分をまとめてハンドラとして独立させ(モジュール化)、それを呼び出して使うことでコードが読みやすくなりますが、これは実行速度の犠牲を伴います。特に、ハンドラを別の階層に置いた場合は呼び出しに時間がかかることになります。しかし、読みやすさを取るか実行速度を取るかは難しい問題で、一概には決められません。非常に汎用性の高いものや、長すぎて一つのハンドラが把握できなくなるようなときにはモジュラーとして分割し、そうでなければ同一ハンドラ内で一気に書いてしまうのがよいでしょう。

*これはひとつのハンドラ内でのモジュール化と列挙の比較を述べたものですが、頻繁に使われるようなコード、例えばファイルを読み出すような機能は、ユーティリティハンドラ(関数)としてバックグラウンドやスタックのスクリプトに置いておくことで、逆に高速化することができます。これは、ハイパーカードが一度呼び出したコードはメモリに残しておくため、別のスクリプトで同じ機能を使う場合、既にメモリにあるコードが使えるようになるからです。

不要な機能をロックする

ハイパーカードはユーザーが命じたこと以外に様々な仕事をしています。これらのうち、スクリプトの実行中には必要がないものはロックしてしまい、スクリプトの実行に専念させることで効率を上げることができます。代表的なものとしてはつぎの3つがあります。

画面の描き替え

プロパティlockScreenをTRUEにセットすると、スクリプト実行中の画面の描き替えを行わなくなります。多くのカードを巡回してデータを拾ってくるようなスクリプトの場合は大きな効果があります。lock screenとしても同じです。

open、closeメッセージ

スタック、バックグラウンド、カードを開いたり閉じたりするたびにハイパーカードはopenCardやcloseStackのようなメッセージを自動的に発行します。プロパティlockMessagesをTRUEにセットすると、スクリプト実行中、これらのメッセージが送られなくなります。多くのカードを巡回する場合、それぞれのカードを開くたびにopenCardハンドラが実行されると大変時間がかかります。これらのハンドラが不要ならばこの手続きをとることができます。 lock messagesとしても同じです。

リーセントカード

カードをめくるたびにメニューの「ゴー/リーセント」で表示するためのカードの位置と縮小表示状態を記録していますが、lockRecentをTUREにするとこの記録を停止します。前2者ほどの効果は期待できませんが、害を及ぼす危険も少ないので、複数カードを移動するスクリプトの場合はロックしておくと良いでしょう。lock recentとすることもできます。

読みやすくメンテナンスしやすいスクリプト

これはスクリプトを速く実行するための手段ではありませんが、大変重要なことです。どんなプログラミング言語でも、読みやすいプログラムを書くことは、高速なコードを作ることと同じくらい重要視されます。どんなプログラムでも完璧ということはなく、必ず後で修正を加えたり機能を追加したりするようになりますが、このとき読んで理解のできないプログラムだと労力が何十倍にもなるからです。

ハイパートークは比較的自然言語に近い書式になるよう工夫されてはいますが、書き散らしたコードが読みにくいことは他の言語と違いありません。むしろ、気楽にスクリプトを書いて実行できるインタープリターであるがゆえに、書きっぱなしになる危険が大きいとも言えます。

読みやすいコードを書くには

ことが大切です。書いているときは分かっているつもりでも、数週間もすれば何のための命令だったのか分からなくなるのが普通です。コメントはこまめにつけましょう。また、作成日付と目的をコメントとしてスクリプトの先頭に加えておくのも役に立つ方法です。