前回の続きです。前回と同じく、Ameba Blog ではプログラムを本文内に書くのは現実的に無理なので、Jugem の方に置いたものをご覧ください。

あまり特殊なテクニックは使わない (2)

ちなみに、ここで出てくる「ふ」というのは私、「U」というのは、私のアシスタントでPC-286U というパソコンで動作している人工知能という設定のUちゃんです。その昔、プログラマーズフォーラムという所で会話形式の発言を書く時に登場してから十数年たちますが、まだ健在です。

@niftyにあるフォーラムも何と健在で、前夜のchatはN-BASICの話とかで超少人数で盛り上がっていたような感じです。プログラムの変数名はどう付けるかというネタなのですが、そういえば DEFINT というのがあったな、とかいう話。
AD
27日から、HTMLタグの一部が使えるようになったというのですが、肝心の<pre>が使えないとプログラムを書くのは殆ど不可能に近い状況になるわけで、今回のは流石に Ameba Blog に書くことに挫折しました。ということで、Jugem にある「裏表」というブログの、「あまり特殊なテクニックは使わない」をご覧ください。

というか、&とかタグを表す文字列そのものを文章中に書きたい場合にどうすればいいのか分からないので困ったものです。以前は、普通に& と書いておけば&と表示してくれたのですが、「<」「>」あたりの文字を使っても本文が途中消滅しなくなった頃から使えなくなったような気がします。
AD
GUIのボタンをプレスした後でキャンセルするためには、次のような手順が必要だ。

ボタンを離すという行動を取り消す。
ボタンの外に移動する。
ボタンを離す。

マウスをクリックする操作にかける時間は個人差が大きい。特に、GUIの設計時点では、クリックという操作ができない人がいることも考慮する必要があるが、今回はクリックできるという前提で考えることにする。実測した訳ではないので未検証であることを先に断っておくが、通常は、クリックしようと思ってから実際に操作完了するまでの間が300~500ms程度ではないかと予想する。つまり、1秒間に、2~3回程度のクリックができるということである。これは、実際にそのような操作が行われていることを観察して予想した値で、それほど的外れではないはずだ。

ただし、この時間は、クリックしようと考えてから、実際に指が運動を終えるまでの時間になっているはずだ。脳が何かを指示して、それが筋肉に伝わるまでには、ある程度の時間が必要なのである。

ここで、クリックという操作が一連の処理として実行されていることに注意してほしい。つまり、クリックしようという時には、ボタンを押そうとか、今押したから離そうとか、いちいち個別に考えない。「押す」と「離す」を一瞬のアクションとして、一度に行うのだ。

※具体的には、脳が「押せ」という指示を出すと、その結果が出るのを待たずに直ちに「離せ」という指示が出ているものと思われる。神経を情報が伝達する速度は意外と遅く、100mm/秒程度といわれているが、実際、それに比べてごく短時間内で複雑な操作ができるのは、そのためであろう。

ということで、実際にボタンがプレスされてからリリースされるまでの時間は、もっと短い。おそらく100msを切る程度だと思われる。

スポーツ選手が「パン」というスタートの合図を受けてから足を動かすまでの時間が約300msだそうである。これを全身反応時間と呼んでいるが、普通の人はもっと時間がかかって、約350ms~400ms程度であることが知られている。ただし、これは音を聞いてスタートするというような、単純な行動の場合の話である。画面の表示を見て、それに応じて行動を切り替えるといった、反射的にはできないような要素が含まれている場合には、脳がそれを考えるための時間が無視できない長さになるだろう。

ボタンが押された瞬間に画面表示が変化したとして、そこで「アッ」と思ったとして、その時点からキャンセルするための指示が指に伝わるまでに少なくとも数百msかかるのだ。ということは、ボタンが押されてから離されるまでの時間が100ms以下、とても間に合わない。

しかし、実際にボタンを押すのを取り消したぞ、という人も結構いると思う。もしクリックという操作を意図的に取り消せる人がいたとしたら、それは超人的な反射神経を持っていると考えるよりは、ボタンを押す時点で最初から何となく怪しいと思っていて、とりあえずボタンを押してみて、なおちょっと考えてみた、というような状況ではないだろうか。これはある意味フライングのようなものだ。しかも、ボタンを押した時点で離すという意図が弱い。つまり、これはクリックという操作を取り消したのではなくて、単にボタンを押してから考えているだけなのだ。

ここで話が最初に戻る。皆さんは、パソコンの画面のボタンを押したつもりなのに押されていなかった、という経験をしたことはないか。この謎を解くには、もう一つの重要なポイントがある。なぜユーザーはボタンを押したつもりになってしまうのか?

(つづく)
AD
最近、オリンピックや世界選手権の短距離走で、フライングで失格になる選手が目立つようになった。その原因は、フライングの判定にある。昔は、スタートの合図よりも先にスタートすることをフライングといったのだが、最近は、スタートの合図より後にスタートしてもフライングになることがあるのだ。

人間が刺激に対して反応して、さらに行動するには、刺激が感覚器官(例えば目や耳)に入った後に、次のようなプロセスを経ることになる。

感覚器官が刺激を神経に伝える。
神経を経由した情報が脳に伝えられる。
脳が刺激に対してどのように行動するかを決める。
脳が行動するための指示を神経に伝える。
神経を経由した情報が目的の筋肉に伝わる。
指示に応じて筋肉が動く。

情報が神経を伝わるのには時間が必要だ。判断するにも時間はかかる。筋肉が動くのも時間がかかる。ということで、このような多段階の処理が完成するには、結構時間がかかりそうだ、ということは想像できるだろう。

選手がもし合図を聞いてからスタートしたのなら、もちろん、スタートと全く同時に足が動くなどということは、人間としてあり得ないのだが、それだけではない。足が動くまでの時間があまりにも短いなら、スタートを聞く前に足を動かそうとした筈で、それはフライングだろう、という理屈なのだ。

余談だが、私見としてはこれはちょっと納得できない。合図を聞こうが聞くまいが、合図の後にスタートしたのだから別に構わないと思う訳だが…。

まあそれはおいといて、GUIのボタンを押してキャンセルするという操作に、この反応時間という考え方を当てはめてみると、どうか。通常の速度でクリックしていたら、ボタンを押した瞬間に「しまった」と思っても、既に遅いのだ。取り消そうと思ったときにはクリックの処理が終わり、ボタンから指が離れた状態になっているのである。

(つづく)
唐突だが、皆さんは、パソコンの画面上のボタンを押したつもりなのに、実際は押されていなかった、という経験をしたことはないだろうか?

前回は、スイッチの操作が「押す」「離す」の2つのタイミングで処理されていることを紹介した。今回は GUI のボタンがクリックされたときに、コンピュータがこの2つのタイミングどう処理すればよいかを考えてみる。

GUI というのは、Graphical User Interface (グラフィカルユーザーインターフェース)のことで、コンピュータグラフィックを駆使して、画面上にボタンなどを表示して、主にマウスを使って操作するようなユーザーインタフェースを指す。もちろん、グラフィックを使っていれば GUI なわけで、ゲームコントローラーを使って操作する大半のゲームは、プレイヤーは GUI を使って操作していることになる。

あるボタンがクリックされたことを、コンピュータはどのように判定すればよいか。単純に考えてみると、ボタンの内部でクリックされたかどうかを調べたらいい筈だ。ちなみに、クリックという操作は次の2つの条件が成立するかどうかで判定できる。

(1) プレス時刻とリリース時刻の間隔が、一定の時間より短い。
(2) プレス位置とリリース位置が同じか、ごく近くである。

(1) は「短時間に押して離す」ことを意味する。(2) は、今まで「プレスした位置とリリースした位置が同じ」と説明していたことと、内容としては同じである。今回、初めて具体的に条件を書いたわけだ。マウスのマウスの構造上、押した位置と離した位置が完璧に一致することは期待できない。そこで、多少ずれた程度は見逃して、同じ位置で操作されたことにしてしまうのである。

これだけなら何も問題はないのだが、GUIのボタンにはキャンセルという機能が付加されている。ここに、押したはずのボタンが押せていないというトラブルの原因がある。

通常、メカニカルなスイッチは、押したら最後、基本的に、キャンセルできない。特に、プレス時点で機械を動作させるスイッチは、原理的にキャンセルすることは不可能だ。ところが、GUI のスイッチは、現実のスイッチではあり得ない操作を割り当てて、ボタンを押す操作を途中でキャンセルできるように工夫されている。

※ボタンを離したときに動作する場合は、最初のボタンを押したままで他のボタンを押した場合にキャンセルするような作り方が可能である。

本物のスイッチは、ボタンをプレスしたら、リリースしないでその指をボタンから離すことはできない。しかし、GUI のボタンは、マウスのボタンを押したままの状態で、位置だけをボタンの外に移動することができる。この操作をキャンセルに割り当てることができるのだ。

ところで、なぜ押したボタンをキャンセルする必要があるのだろうか? 確かに、ボタンを押した瞬間に「しまった」ということは、あるかもしれない。ならばキャンセルできれば便利だろう、と考えたのかもしれない。一見それは合理的だが、ところが、ここに一つとんでもない誤解がある。

(つづく)
前回、クリックのひみつ (1)で、クリックという操作は、2つの条件を満たさなければならないことを紹介した。つまり、プレスからある時間内にリリースすること、プレスとリリースの位置が同じであること、この2つである。

ここで、ある時間というのは、通常、かなり短く設定されている。どの程度かというと、おそらく数百ミリ秒以内、つまり、1秒よりも短い時間になっているはずだ。マウスの操作はクリックだけではない。他の操作と区別するために、このような短い時間を指定する必要があるのだ。

例えば、ドラッグという操作がある。これは、プレスして、マウスを動かして、別の場所でリリースする、という一連の操作である。と書けば簡単だが、実は意外とややこしい。同じ場所で離すとダメなのか、とか、プレス、移動、リリース、という操作を一瞬で行ってもドラッグしたことになるのか、という話が出てくるのだ。

その前に、一般的な機器がどのようにスイッチの操作に対応しているか紹介しておこう。スイッチは「押す」「離す」の2つの動作を行うことのできるユーザーインターフェースであるから、その両方について、機器が別の動作をするように作ることができるのだ。

典型的なのは、昔のドアチャイムである。玄関に付ける、ピンポ~ンと鳴るアレだ。今だと電子式だから分かりにくいが、昔はチャイムは電磁石を使って板を叩くという機械だった。

仕組みはこうである。スイッチを押すと電流が流れる。電流が流れると電磁石が稼動し、アームが引き寄せられて、板に当たる。このときに「ピン」という音が出る。スイッチを押している間は、この状態が持続する。スイッチを離すと電流が切れる。電流が切れるとアームが元の位置に戻るのだが、このときに、別の板に当たって、「ポン」という音が出る。つまり、スイッチを押した時に「ピン」、離したときに「ポン」という音が出るように設計されているのだ。

もう一つ紹介しておこう。エレベータの「開」ボタンの動作である。

エレベータの「開」ボタンを押せばドアが開く。当たり前だ、と思っている人がいるかもしれないが、そりゃもう当たり前である。では、離せばドアはどうなるか?

ドアが閉じるというのは、完全な正解ではない。確かにドアは閉じることもあるが、閉じないこともある。というのは、エレベータは、特に何もしなくても、しばらく待てば勝手にドアが閉じるように設計されているのだ。どの程度待てば閉じるかというのは、エレベータによって設定が異なる。小さいエレベータだと、3~5秒程度待てば勝手に閉まるようになっている。大きいエレベータになるほど、多人数が乗り降りすることを想定して、この時間は長く設定されているはずだ。

エレベータの「開」ボタンを押してすぐに離せばどうなるか。ドアは閉じない。つまり、「開」ボタンを離しただけでは「ドアを閉じる」という処理が実行されないことがある。実は、エレベータのドアは、自動的に閉まる設定になっていると、次の2つの条件が成立したときに閉まるようになっているのだ。

・開ボタンが押されたか、あるいはドアが開いてから一定時間以上経過している。
・現在開ボタンが押されていない。

この一定時間というのが、開ボタンが押された瞬間から計測するようになっていることに注目してほしい。

無人のエレベータに乗ったときに、ドア係になってくれる人がいる。スイッチの一番近くの人がドア係になる慣習もあるようだが、この人が「開」ボタンを押していて、しかも、この階で降りる場合、これが要注意である。

私も何度かヤラレたことがあるが、もしドア係の人が出る瞬間まで「開」ボタンを押しているとどうなるか。押している間はドアは開いている。しかし、この人が出るときに何も考えずに「開」ボタンを離すと、その瞬間にドアが閉まり始める。既に、ボタンを押してから数秒以上経過しているからだ。たいてい、ドア係の人はドアをうまくすり抜けて出ることができるのだが、その後に乗ろうとしている人の目の前で、ドアは閉る。

エレベータに乗るときにこのような悲劇を避けるためには、最後の人が出てくるまで、エレベータの外のボタンが押せる位置に立っていればいい。ドアが閉まり始めたら、ボタンを押せばいいのだ。外のボタンを押せば、その時点でドアが開いて、さらに数秒の猶予期間が与えられる。

ドア係になった人はどうすべきか? 最後に自分が出る場合は、出る前に開ボタンをクリックしてやればいい。開ボタンを既に押していたなら、一度離してからクリックするのだ。すると、ドアが閉まるのは、ボタンを押したときから数秒後になるから、その間に外の人はエレベータの中に入ることができる。

(つづく)

2004年11月号のクイズ (解答)

テーマ:
クイズの解答。わざと黄色を指定して見にくくしてあるので、選択して反転するか、他のウィンドウにコピーして読んでください。

fromIndex がこの文字列の長さよりも大きい場合に、部分文字列として空(長さ0)の文字列を指定したときの結果を文字列の長さにするため。

解説。まず、Math.min(fromIndex, this.length()) の性質を考える。this.length() という条件を追加することで、fromIndex が元の文字列の長さよりも大きい場合、k の値は、fromIndex 以上ではなく、this.length() 以上で調べることになる。

例えば「phinloda」という文字列の場合、this.length() は8だから、8より大きな fromIndex を指定したときも、この処理仕様としては、8番目以降に指定した部分文字列があるかどうかを調べることになる。

しかし、「phinloda」という文字列の長さは8だ。最後の「a」ですら、最初を0として7番目の文字である。8番目以降を調べるというのはどういうことか?

public boolean startsWith(String prefix, int toffset)

の仕様は、リファレンスマニュアルには次のように書いてある。

toffset が負の値の場合、あるいは String オブジェクトの長さより大きい場合、結果は false。そうでない場合は、結果は

    this.subString(toffset).startsWith(prefix)

の式の結果と同じ


8というのは「phinloda」の長さに等しい。「より大きい」ではないのだ。だから、結果は、this.subString(toffset).startsWith(prefix) を調べることになる。

しかし、this.subString(toffset) は空の文字列である。空の文字列にマッチする部分文字列など存在するだろうか? それが一つだけあるのだ。空の文字列は、空の文字列の部分文字列なのである。つまり、"".startsWith("") は trueなのだ。

要するに「Phinloda」という文字列に対して、indexOf("", 10000) は 8 なのである。indexOf の仕様が、もし

k >= fromIndex && this.startsWith(str, k)

という条件で調べるようになっていたら、「Phinloda」という文字列に対して、indexOf("", 10000) は -1 になってしまう。

ちなみに、これがどういう意味を持つかというと、ある種のアルゴリズムを実装するのに都合がいいらしい。具体的にどういう場合に便利なのかというと、えーと、分からないです、実は。すみません。考えておきます。

2004年11月号のクイズ

テーマ:
C Magazine 2004年11月号の「あっぱれご意見番」に出したクイズの話。

問題について簡単に紹介しておく。このクイズは Java を知らないと解けないのだが、まず、Java を知らない人向けに大雑把なコンセプトを紹介しておく。

ある文字列の指定位置以後に部分文字列が含まれていればその位置を返す処理を考えてください。但し、指定した部分文字列が見つからない場合は、-1を返すものとする。位置は、文字列の最初を0番目とする。ま、ありふれた処理ではあります。

具体的に考えてみよう。例えば「phinloda」という文字列の、0番目以後に「in」がふくまれているかどうか。0番目というのは日常生活では違和感のある表現だが、プログラムでは割と使われるもので、ここでは先頭を意味している。この文字列の2番目から「in」という文字列が現れるから、この場合は、2という値を返せばよい。

2番目以後が指定されたらどうか。2番目以後というのは、pが0番目、hが1番目なので、2番目の文字「i」以後だけを調べることになる。ちょうど2番目から始まる文字列が「in」だから、この場合は、2という値を返せばよい。

3番目以後が指定されたらどうか。「phinloda」という文字列の3番目以後には「in」という文字列は存在しない。この場合は、-1という値を返すことになる。

このような処理を書くのは、効率を考えなければ簡単だ。実際、Java の String クラスにはその処理が最初から用意されていて、indexOf という名前が付いている。ということで、ここからが本題になってくるのだが、とりあえず、Cマガジンのコラムの最後に書いた問題部分を紹介する。

Java 2 SE 1.4.0 のリファレンスマニュアルには、String.indexOf というメソッドが次のように説明されている。

----

public int indexOf(String str, int fromIndex)

指定されたインデックス以降で、指定された部分文字列がこの文字列内で最初に出現する位置のインデックスを返します。返される整数は、次の最小値 k になります。

k >= Math.min(fromIndex, this.length()) && this.startsWith(str, k)

このような k の値が存在しない場合、-1 が返されます。


----

なお、実際のマニュアルは、this.length() ではなく str.length() と書いてあるかもしれないが、Cマガジンに書いたように、これは誤りで this.length() が正しい。

問題は、なぜ

k >= fromIndex && this.startsWith(str, k)

ではなく、

k >= Math.min(fromIndex, this.length()) && this.startsWith(str, k)

という条件になっているのか、というものだ。なお、この問題は一般的なものではなく、Java 特有の振る舞いを知らないと解けないので、念の為。

(つづく)
前回のコラムで、クリックができない人もいるという話を書いた。クリックとはどういう操作のことか、説明できるだろうか。よく「ボタンをポンと押す」と言う人がいるが、この表現だと、感覚的には分かりやすいかもしれないが、分かったような分からないような、というか、分かる人には分かるといった感じだ。

ボタンを押すことをプレス(する)という。押した状態のボタンから離すことをリリース(する)という。一般には、ボタンを押すという操作は、ボタンを押してから離すところまでを指すことが多い。「ボタンを押してください」といえば、特別な状況でない限り、そう指示された人は、ボタンを押して、それから勝手に離すのである。離せといわれるまで押し続ける人はあまりいない。

ボタンをプレスしてからリリースするまでの時間は、かなり短いのだが、クリックというのは、それをさらに短くした、一瞬の操作になる。一瞬というのはどの程度かというと、0.1秒とか、そのようなオーダーの時間だ。

なぜクリックができない人がいるのか。日常生活で、クリックしないと操作できないような場面は少ないのである。今でこそ、マウスのボタンはクリックという操作を行うものとして普及しているが、マウス以外に、クリックしないと操作できないようなモノが何かあるだろうか? 大抵のボタンは、押してから離すまでの時間がどうであれ、動作にはあまり関係ないのだ。

※長時間押し続けることに特別な意味を持たせた電子機器がある。例えばノートパソコンの電源スイッチを数秒間押していると強制終了になる、といった具合。

考えてみると、クリックで操作するようなものがどうも思いつかないが、あえて言うならば、ある種の楽器はそうかもしれない。特に、打楽器系の楽器は、押したままの状態にすると音が出なかったり、こもってしまったりするため、叩いた瞬間にすぐ離すということが重要になる。

昔の機械式のタイプライターは、キーを押したままにすると機構上うまく操作できなかったため、キーを押してすぐ離さなければならなかった。これはクリックに近い操作である。今はキーボードが電子式になったので、キーを押したままでも特に不都合はないはずだが、操作上の都合とか、素早く文字を入力するためには、押した瞬間に離した方がいい。これもクリックに近い感覚かもしれない。

さて、マウスの場合、実はクリックという操作には、もう一つの条件が加わっている。マウスは、動かしながら操作することができる。これが曲者なのだ。つまり、クリックというのは「マウスを動かさずに」という条件で操作しなければならない。マウスをクリックできないという人は、これが難しいらしい。ボタンを押したらマウスが動いてしまうのである。

そんなに力を入れなくてもマウスのボタンは押せるのだが、力を入れずに押せと指示したら、「力を入れないと押せないじゃないか」みたいな。余計混乱して訳が分からなくなってしまうようだ。

(つづく)
ダブルクリックという操作がある。マウスを使ったユーザーインターフェースとしては定番だが、ダブルクリックでしかできない操作を作ってはいけない、という原則がある。ダブルクリックができないユーザーがいるからだ。

ここで「ダブルクリックができない」というのは、難しいという意味ではない。本当にできないのである。中にはクリックができないという人もいる。これも本当にできないのである。クリックという操作は、押す+離すという二つの操作の組み合わせで代替できることが多いし、タイミングを調整すれば何とかなるのだが、ダブルクリックをタイミングの調整だけで対応するには限界がある。

先日、ある駅のホームに上る階段の下で、おばあさんと駅員がモメていた。おばあさんは階段から上ろうとしているのだが、駅員は、この階段は下り専用だから、横にあるエスカレーターを使えと言っている。ちょうどラッシュ時で、大勢が下りてくるから危ないというのだ。しかし、結局、おばあさんはそれを無視して、ゆっくり階段を上り始めた。駅員は危ないからやめるように根気よく説得していたが、おばあさんは流れに押されつつも、何とか無事ホームまで上りきってしまう。

横で見ていただけなので細かいことは分からない。ただ、想像できるだけである。このおばあさんは、エスカレーターを使うことができなかったのではないだろうか。

エスカレーターが使えないお年寄りがいることは皆さんご存知だろうか? 乗り降りのタイミングがつかめなくて、乗れないのである。無理に乗って怪我をすることもあるらしい。エレベーターを使うのが最も安全だが、どの駅にもあるとは限らない。結局、階段を使うことになる。お年寄りは足腰が弱いから階段から上れないだろう、などと勝手に思い込んではいけないのだ。多分、このおばあさんも、何か理由があってエスカレーターが使えなかったのではないかと思う。

自分には簡単なことだから誰にでもできると思い込むのは危険である。自分には簡単なことでも、他の人にとっても簡単であるとは限らないのだ。

なお、問題の駅は、逆側に上り下りができる階段がある。駅員は、エスカレーターを使えと指示するのではなく、別の階段を使って上ってくれと言うこともできたはずだ。確実な代替手段を示すことも重要である。