マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

グルー値文字列中の空白トークン

お待ちかねの(誰が?) TeX パズル。

次のそれぞれのトークン列について、先頭から「グルー値」を 1 つ読んだ場合、どこまでが「グルー値を表す文字列」と解釈されるか。言い換えると、これらの前に \hskip を置いた場合、どこまでのトークン列が「その引数として読み込まれる」ことになるか。ただし、〈~〉は空白トークン(カテゴリコード 10)を表すものとする(あるいは、アクティブな〈~〉に対して \def~{ } の定義がされていると考えてもよい)。また、数字や英字は通常のカテゴリコードをもつとする。
  • 1cm~plus~~1cm~~x
  • 1cm~minus~1cm~~x
  • 1cm~plus~~1fil~~x
  • 1cm~minus~1fil~~x

答えを述べる前に、「実際に確認する方法」を説明しておく。例えば次のようにすればよい。


\catcode`\~=13 \def~{ } % <~>の定義
\def\check#1\nil{\message{[#1]}}
\afterassignment\check\skip0=1cm~plus~~1cm~~x\nil

結果は以下のようになる。

  • 1cm~plus~~1cm~~x
  • 1cm~minus~1cm~~x
  • 1cm~plus~~1fil~~x
  • 1cm~minus~1fil~~x

何故そうなるかは、グルー値文字列の書式の完全な形を考えれば想像がつく。


1☆cm☆plus☆2☆cm☆minus☆3☆cm※

ここで各要素(キーワードや数字列)の間(☆の位置)には任意個の空白トークンを挟むことができる。そして、整数文字列と同じく、末尾(※の位置)には終結のために 1 個の空白トークンが置ける。これを基に考えると、「plus 1cm」の後ではまだ途中(後ろに minus があるかも知れない)なので、続く空白を全て読み捨てた(x が出現した時点で、その直前で終結させた)のに対し、「minus 1pt」の後では、もう既に全ての要素を読み終わっているので、「終結のための 1 個の空白」だけを読み捨てたということになる。

この説明では一番最後の「minus 1fil」の動作が明らかに辻褄が合わないように見える。しかし、実はそうではないのである。これは次の例を見れば解る。

  • 1cm~plus~~1fil~~l~~x
  • 1cm~minus~1fil~~l~~x

この場合、グルー値文字列になる部分は以下の通り。

  • 1cm~plus~~1fil~~l~~x
  • 1cm~minus~1fil~~l~~x

要するに、TeX では「fill」は 1 つのキーワードではなく、「fil」「l」の 2 つのキーワードの列なのである。従って、「fil~~l」でも有効となり、また「minus 1fill」まで読んでもまだ途中(先に「l」があるかも知れない)だから結果的に全ての空白が読まれることになる。((「1filll」(これより高い無限長はない)まで読んでもなお次の「l」はキーワードとして読まれて、そこで「Illegal unit of measure (replaced by filll)」のエラーになる。このエラーを見かけたら、是非とも「h」でヘルプを出してみよう。))

ここで話は終わりではない。実は、LuaTeX では結果が異なるのである。

  • 1cm~plus~~1cm~~x
  • 1cm~minus~1cm~~x
  • 1cm~plus~~1fil~~x
  • 1cm~minus~1fil~~x
  • 1cm~plus~~1fil~~l~~x
  • 1cm~minus~1fil~~l~~x

何故この結果になるかは、「cm」と「fil」の扱いが同じであることに気づけば解るであろう。つまり、LuaTeX では「fill」を複数のキーワードの列ではなく単一のキーワードとして扱っているのである。正確に言うと、LuaTeX では、「fi」「fil」「fill」「filll」が別個のキーワードで「l」というキーワードは存在しない。*1ゆえに「fil l」では fil が単位となり、次の l を見てその前で終結することになる。

*1:なお、「fi」は「fil」より低位の無限長である。Omega で導入され、LuaTeX にも引き継がれている。