2014/11/10

TeXユーザの集い2014でしたTUG2014の話

プレゼン資料だけだとあまりに情報量がなさそうなので、発表の準備のときに用意していた原稿も下の方に張り付けておきます。未編集です。



自己紹介
======
こんにちは、鹿野です。ふだんは神田の技術系出版社で書籍の企画をしたり編集をしたりしています。
むかしは半分趣味でTeXマクロを書いて遊んだりもしていましが、最近はもっぱら仕事上のおつきあいという感じです。
その仕事では、バージョン管理している原稿から印刷所に入稿するPDFを自動組版するのに、pLaTeXを使っています。

今日は、TUGのアニュアル・ミーティングという、この「TeXユーザの集い」のワールドワイド版みたいなものに参加してきた話をしたいと思います。
今年の集いは、ライトニングトークまで含めて、ものすごく濃厚な内容になっていますが、私の発表は技術的な内容は薄い、文字通りの体験記なので、気楽に聞いていただければ幸いです。

TUGについて
======
まず、そもそもTUGって何、という話から入ろうと思います。
TeXのユーザー会は、実は世界のいろいろな地方ごとにあって、その北米版が「ユーザーに「s」がつくほうのTeX Users Group」で、その略称がTUGです。
ちなみに、日本はこのオフィシャルなTeXのユーザー会がないんですよねー
今回の「集い」で事前に日本のTeXユーザーの組織化に関するアンケートがあったと思うんですが、このTeXユーザーグループに名を連ねようという布石なのかもですね。
いや、知らないんですが。

で、北米版のTeXのユーザー会であるTUGが、全世界のTeXユーザーグループでもあるという形になっています
ので、TUGの会員には、北米在住でなくても、年会費を払えば地球のだれでもなれます。わたしも一応TUGの会員に「個人で」なっています。年会費は$105です。
この年会費は、みなさんご存知のCTANの運用にも使われているらしいので、仕事とかでTeXを使っているので還元したい方は会員になってみるとよいかもしれません。

TUGboat
======
TUGの会員になると、TUGboatという雑誌が送られてきます。年に3回です。
これの編集と発行もTUGのお仕事です。
まあ、1年たつとすべての記事がオンラインで誰でも読めるようになるので、TUGboatを読みたいだけならば、会員になったりしなくても大丈夫です。

TUG年次総会
======
で、ここからだんだん本題になるんですが、TUGのもっとも大きな役割としてあるのが、世界中のTeXユーザーの交流の場となる、年に一回のイベントの主催です。
去年は、この中で参加した方も多いと思うのですが、東京で開催されました。通算、第34回だったんですが、日本では初めての開催だったということです。
150人くらいの参加者だったそうで、大成功だったようです。
私も、去年ちょこっとかかわらせていただいて、それがすごく楽しかったので、今年もぜひ参加したいと思って参加したのが、アメリカのポートランドで開催された第35回のTUGであるTUG 2014になります。

ポートランドについて
======
ポートランド、あんまり日本人にはなじみがない気もするので、ちょっとだけふれておきたいと思います。
ポートランドは、オレゴン州の北西にあって、オレゴン州でいちばん大きい都市です。
で、私もぜんぜん知らなかったんですが、TUGの本部がある場所なんだそうです。そんなわけで、過去に何回か、ポートランドでTUGが開催されてるみたいですね。

これも知らなかったのですが、ポートランドはバラの街としてしられているらしいです。
あと、自転車の環境がすごく整備されていて、みんな路面電車に自転車をもちこんだりしてるんですね。
街の真ん中にすごく大きな川、ウィラメット川が流れていて、写真にあるような鉄橋をはじめ何本も橋がかかってるんですが、それらにも自転車用の道が整備されていて、びゅんびゅん行きかってました。
自転車って、盗難とかコワイので、治安がよい場所じゃないとあんまり流行らないわけですが、実際、ポートランドはアメリカでも最も治安がいい街のひとつなんだそうです。
なので、まあ日本で夜に出歩くほど気楽というわけじゃないですが、夜になって飲みに繰り出しても、そんなに恐ろしくない町でした。
しかも地ビールの醸造所が何カ所もあるんですね。直営のバーとかもいっぱいあったり、これはもう夜は飲みに行くしかないだろう、という感じで、

ビール
======
しかも、ちょうどTUGの前日まで地ビール祭が開催されてたらしくて、私は間に合わなかったんですが、
前入りしてた黒木さん、去年のTUG2013の実行会長ですが、その黒木さんは、この地ビール祭であびるほどビールを飲んでたらしいです。
私のほうは、発表にも申し込んでいたので、飛行機の中とかで必死に発表の練習をしてたわけですが、その間にすっかりポートランド観光してたと。
この写真も、そんな黒木さんが私が到着する前に見つけてくれていたスーパーのビール売り場の様子です。
この一角だけでもこれだけの銘柄の地ビールが並んでいて、それでちゃんと経営が成り立ってるわけですね。
こう、アメリカっていうと、大量消費・車社会・治安も悪い、みたいな印象もあったのですが、ポートランドに限って言えば、すごく成熟した素晴らしい町でした。

会議の場所
======
そんなポートランドのダウンタウンの片隅に、マークスペンサーという小さいけれどいい感じのホテルがあって、そこの地下が今年のTUGの会場でした。
この写真の正面が下に続く階段になっていて、その奥の会議室が会場です。

ホテルなので、ほとんどの参加者は、私や黒木さんも含めてそこに滞在もしてたみたいです。
だから、本当に朝から晩まで、会議中もそれ以外の時間も顔を合わせるわけですね。

あと、このマークスペンサーホテルのよいところとして、すぐ近くに世界最大のチェーンじゃない書店があります。
TeXのユーザーの集まりなので、本とか大好きっこばかりが集まってるわけだから、みんな空いた時間にこのパウエルズという本屋さんに遊びにいくわけです。
とにかくすごい大きい本屋なのですが、誘い合わせていったわけでもないのに、ばったりほかの参加者にあったりもしました。

Powell's
======
この写真はパウエルズの店内で撮ったもので、まあ、宣伝です。
弊社で作ってる「マンガでわかる統計学」とかの英訳版がこんなふうに面出しされていて、かなりうれしかったです。
こっちはTeXの本のコーナーで、世界最大でもこれしかなかったので、ちょっとさびしい感じですね。

プレゼンの話(概要)
======
TUGの会議そのものは、一言でいってしまうと、プレゼン大会です。
3日間にわたって、35分ずつ、一日10本くらいのプレゼンがあって、大部分の参加者がすべてを聞いていたと思います。
実際、参加者の半分以上がプレゼンもするという構成です。

プレゼンの内容は、ものすごく自由で、TeXに関係するツールの話もあれば、パッケージの紹介もあれば、TeXでこんなことしてみましたという事例の話もあります。
いろんなツールやパッケージの開発者もいるので、こういうこと考えてよとか、そういう話もありました。
全部のプレゼンについて一つずつ説明していくのは時間的にも能力的にも無理なので、詳細はTUGのWebや、最初に紹介したジャーナルのTUGboatでチェックしてみてください。
プレゼンターが直接書いた記事があるものもあるし、TUG 2014全体の要約記事なんかもあります。
もし気になったのがあったら、聞いてもらえれば、ひょっとしたら「こんな話だったよ」と言えるかもしれません。

なので、いくつか個人的に気になったプレゼンをピックアップしてみたいと思います。

Astonishing
======
まずはなんといっても、JSBoxです。
これ、名前から何なのかまったく想像できないんですが、TeXのCによる再実装です。
WEB2Cのアウトプットを書き換えたとか、そんなちゃちなものじゃなくて、ゼロからこのダグ先生がCで書き直して、トリップテストも機能的にはパスさせてしまったというやつです。
ダグ先生の会社は、数学チックで再帰的な図案のデザインとかMacのコンサルとかやってるらしくて、
なんでCなんだよ、っていうのが個人的な感想ですが、それはまあおいといて、すごいですね。
内部的には21ビットのUnicodeで、それ向けにオリジナルTeXのアルゴリズムやデータ構造を全部再実装したらしいです。エンコーディングにはUTF-8を採用と言ってました。
OpenTypeフォントがなんでも使えるといってるので、そのまま日本語も使えるといいよねーという感じなんですが、TeXのcatcodeを勝手に拡張しちゃっていて、
catcode 16を名前空間のセパレータ用として予約しちゃっているんですね。なのでptex 向けのマクロやスタイルは全滅かもしれません。
「今度のTeX Liveにいれよう」みたいになってたので、そのうち使えるようになったら試してみたいですね。

もうひとつ会場を沸かしてたのは、カベーさんの発表です。
GUIでパラメータが設定できるようになっていて、そこの数値をスライダーとかで動かすだけでLaTeXのページ版面とか簡単に設定してしまうのをデモされてました。
いろいろなパッケージを内部で階層的に管理している部分がポイントらしくて、原理的には、なるほどねーという感じなんですが、実際デモを見るとすげーっていう感じでした。

Interesting
======
個人的に興味をもって聞いてたのは、TeXと、印刷用じゃない出力との関係についてのプレゼンでした。

日本だと割とすぐにEPUBとかに走っちゃう印象があるんですが

たとえばPDFのリフロー対応とか、
これ、たとえばdvipdfmxで作ったpdfでもAcrobatでタグ付きPDFとかに変換すると、Adobeリーダーでもちゃんとリフローが機能するPDFになったりするんですが、
pdftexが生成するpdfではスペースの出力がまずくて、全部の単語がくっついちゃってたらしいんですね。それを解消しました、みたいな発表があったり、

まあ、やっぱりほとんどAdobeのリーダー専用なんですが、PDFのアノテーションとかレイヤとかをTeXからもっと使おうぜとか、

あとは、LaTeXの原稿からWebブラウザで「読める」出力を得る方法を模索している話とかもありました。
たとえは、この写真のビルさんは、統計の先生なんですが、XMLと対応できるLaTeXの部分集合みたいなのを定義して、それをここ何年か開発しているとのことなのですが、
その中で数式をMathMLのサブセットとCSSでここまでブラウザ上で再現しました!という発表をされていたり、
これはまた別の人なんですが、アメリカ・インスティチュート・マスマティクスのジャーナルのLaTeX原稿をHTMLに変換した実例をデモする発表をする人がいたり、
これも単に変換するのではなくて、参考文献とか、クリックすると単なるリンク先に飛ぶのではなくちゃんとJavaScriptのアクションで本文の直後に詳細な情報を表示するようにしないとだめとか、そういう話が面白かったです。

あと、TeXとはまったく関係ない話もぽつぽつあって、
数学の教科書用のXMLアプリケーション、XML用語の「アプリケーション」ですね、これを使ってSAGEにも紙にも対応した教科書を作ってる話とか
Plottyっていうグラフ作成のWebアプリがあるのですが、それの中の人のプレゼンとか、
あと、この写真のおじさん、Wikiを発明したウォード・カニンガムです、この人がゲストスピーカーで、新しいWikiを作っていますという話をしたり、
面白かったのは、みんな、「TeXに関係は?」みたいな質問をされるんですよ。その答えが、どれも、MathJaxで数式を表示してます、でした。
たぶん、数式をWebブラウザとかで表示したい場合は、個人的にはビルさんのアプローチとかのほうが好きなんですが、もうMathJaxになるんだろうなあという思いを強くしました

プレゼン外
======
TUGそのものがプレゼン大会だっていうのは、さっき言ったとおりなんですが、
プレゼンだけなら、わざわざ太平洋を越えていかなくても、あとでスライドとか動画を見るのでもいいわけですよね。
というわけで、最後にプレゼン以外の話です。

去年のTUG 2013でもプレゼン外の雑談が有益だってって、寺田さんがブログに書いてらっしゃいましたが、
実際、TUG 2014もそうで、しかも朝から晩まで一緒の空間にいるので、合宿みたいなノリになってるわけですね。
昼休みや休憩はもとより、朝からホテルのラウンジでコーヒーを飲みながら雑談が始まるわけです。
毎年TUGに参加している人たちって、プレゼンを名目に旧交を深めて観光旅行してるだけなんじゃないかと。
とくにこのパブニートさんは、去年のTUGで仲良くなって、今年もすごく仲良くしてくれて、
ほとんどはTeXに関係ない話だったりもするわけですが、こういう雑談の時間がすごく有意義でした。
ちなみに、このプレゼンで使っている写真は、ほとんどは彼によるものです。使っていい?って聞いたら快諾してくれました。

夜も夜で、ドイツからきているフランク・ミッテルバッハさん、LaTeX2eの開発者で、LaTeXコンパニオンの著者です、このすごい人が、なんか飲みに誘ってくれるわけですね。
で、こんなふうにレストランでメニューを見ながら、これ何やってるのかというと、みんなでメニューの組版にケチをつけてるんですね。

こっちは懇親会の写真なんですが、こっちの二人はインドから来ているTeXニシャンで、この人は黒木さんです。
プレゼントに当選したときの様子です。隣のクリス・ローリーは、LaTeXコンパニオンの第2版の共著者の一人で、この懇親会の直前にビールをおごってくれました。
懇親会の後も、このクリスと、フランク、それから、こっちの奥にうつっているウィル・ロバートソン、fontspecとかの開発者です、それに黒木さんと私とでビールを飲みに行きました。
いま、こうやって話してても、なんて贅沢な時間だったんだろうと、なんか自分なんかがほんと図々しかったのではないかと恐縮に感じてしまいます。

というわけで、いろんなプレゼンを聞いてきた話よりも、こっちのほうが今日、メインに伝えたいことかもしれません。

2013/12/31

2014年賀状的なもの

今年は喪中だから年賀状じゃないんだからね。

%!
<< /PageSize [285 420] >> setpagedevice

/setrandcolor {
  /m exch .1 add def
  /r1 {rand 5 mod 1 div m add} def
  /r2 {rand 3 mod 3 div m add} def
  /r3 {rand 3 mod 3 div m add} def
  r1 r2 r3 setrgbcolor
} def

/f {% def
    dup dup dup
    4 mod 0 eq {(2) 4 1 roll -1 0 rmoveto} if 
    4 mod 1 eq {(0) 3 1 roll -3 0 rmoveto} if 
    4 mod 2 eq {(1) 2 1 roll -3 0 rmoveto} if 
    4 mod 3 eq {(4 ) -5 0 rmoveto} if
} def

/drawyear { % def
  4 1 64 { % for
    /i exch def
    /v { i i mul i mul 0.0003 mul } def
    /w { i 15 add 2 div } def
    /d { -360 w mul 2 3.1415 mul 65 mul div } def % -360w/(2pi*65)
    d rotate
    /Titania findfont
    2 w mul scalefont setfont
    v 0 rmoveto
    0.001 setrandcolor
    i f show
  } for
} def

1 1 20 { % for
    /n exch def

    93 160 translate 
    135 rotate
    0 0 moveto
    drawyear

    100 rotate
    150 -140 moveto
    0 setgray
    /Palatino-Bold-Italic findfont 12 scalefont setfont
    n =string cvs show
    0 -2 rmoveto (/) show
    0 -2 rmoveto (20) show
    showpage 
} for

例年に増して芸がない感じもするけどシンプルなのがいちばんということで。

2013/12/20

Learn You The TeX for Great Good!
 「第3章 マクロ初級講座」

[前回までのあらすじ]

LaTeX の原稿に記されている文字を別の文字に置き換えて出力したくて TeX マクロを書いてみたけど、なぜかうまく動かない。 TeX はチューリング完全なプログラミング言語だって聞いたけど、こんな基本的なテキスト編集の機能さえ持ち合わせてないのでは話にならない。 TeX も LaTeX も滅ぶべき。

Note:この記事は、TeX & LaTeX Advent Calendar 2013 の20日目の記事です。昨日は zr さんによる「テキストの装飾の指定: CSSとLaTeXの比較」です。明日の21日目は shigeyas さんの予定です。

LaTeX のことはきっぱり忘れてください(「TeX」といったら1982年に書き直されたいわゆる TeX82 と、その直系にあたるバージョン3のバグフィックス版たちのことです)。 ここでは TeX で文字の置換に「近い」ことをしてみましょう。 再帰的な TeX マクロをどうやって書くか、何となく感じてもらうのが本章のゴールです。

再帰!帰!!

いま、与えられた文字列中のすべての文字を「*」に変換して伏字化した結果が欲しいとします。「brainfuck」→「*********」という具合です。パスワードの入力欄みたいな雰囲気ですね。

ぱっと思いつくのは、こんな再帰的なマクロを使った例です。これは実際、上記の例「brainfuck」→「*********」ならうまく変換してくれます。

\def\ast#1{\recur#1!}
\def\recur#1{%
  \ifx!#1\else*\expandafter\recur\fi}

\ast{brainfuck} とすると TeX の中で何がおきるのか、順番に見ていきましょう。 \ast マクロは、引数の末尾に ! をつけてから、ブレース {} を取っ払った状態で \recur に引き渡します。 「 \recur brainfuck! 」という形で \recur マクロに引数が渡されるということです。

この状態で \recur マクロが引数だと思うのは、 brainfuck! という文字列ではなく、先頭の b だけです。 ナンデと思うかもしれませんが、ブレースがない状態でマクロに何かを渡すと、その先頭だけが引数としてマクロに渡されるのです。 これは、TeX において \def を使って定義できるのは、一般的なプログラミング言語における関数とかサブルーチンとかクロージャーとかではなく、「置き換えルール」だからです。

置き換えルールを \def で定義する際には、置き換えの対象となるパターンを指定するときのプレースホルダとして、 #1 のようなパラメータ引数が使えます。 たとえば \def\recur#1{foo #1 bar} によって「定義」されるのは、「\recur の直後に「何か」が一個あったら、それを #1 で示されるパラメータ引数に束縛して、 foobar で囲む」という置き換えルールです。

じゃあ、なぜ最初に \ast{brainfuck} としたときには、文字列 brainfuck#1 に束縛されたのでしょうか? それは、ブレースで囲まれて渡されたパラメータ引数は、そのブレースを取り除いた状態でまるごと束縛されることになってるからです。

トークンとトークンリスト

上記では「何か」とぼかしましたが、置き換えルールが適用される対象は「トークン」と呼ばれています。 トークンとは具体的には何なのでしょうか? 

簡単にいうと TeX のトークンとは、「文字(カテゴリコードという TeX 特有の属性つき)」または「コントロールシーケンス(いわゆる \ ではじまるやつ)」です。 TeXは、読み込んだファイルに含まれる文字や数字や記号を、まずこの「トークン」の列へと咀嚼します。 そうして得られたトークンの列は、トークンリストと呼ばれます。

Note:文字トークンとコントロールシーケンス以外に、マクロ定義に出てくる #1 みたいなパラメータ引数もトークンとみなされてるのですが、忘れてかまいません

このトークンリストには、原稿を書いた人が入力したコントロールシーケンスなんかもみんな含まれているわけですが、それを最終的に出力するデータ用のトークンリストへと展開するのがTeXの次の仕事です。

ここまでをまとめると、 \ast{brainfuck}\ast の定義によって \recur brainfuck! に置き換えられ、 \recur brainfuck!\recur の定義によって \ifx!b\else*\expandafter\recur\fi rainfuck! に置き換えられます。 b だけが \recur に渡された結果、残りの rainfuck! が後ろにくっついているのに注意してください。

\ifx は直後のトークンを比較して展開する場所を制御

\ifx は、直後の2つのトークンが「同じ」なら \else または \fi までを読み込み、違ったら読み飛ばすという TeX の命令です。

さきほど \ast マクロを利用したときに、ブレースを使って引数を {foo} のように囲んでグループとして渡せるといいました。 ブレースで囲んだ「何か」は、トークンではありません。 グループを開始する左ブレースというトークン、ブレースの間にあるトークンたち、それにグループを終了する右ブレースというトークンからなる、トークンリストの一部です。 したがって、たとえば \ifx {foo}\x のようにしても、 x が文字列 foo か調べて条件分岐することにはなりません。 もし \ifx {foo}\x と書いたら、 {f が「同じ」かどうか調べた挙句、当然 {f は「同じ」じゃないので、 \else または \fi まで読み飛ばされて意図しない結果になります。

Note\ifx は直後の2つのトークンをとって、それらを展開せずに、同じかどうかを調べます。 もしマクロ定義の中で \ifx #1! のように書いて(#1! が逆になってることに注目)、展開時に #1brainfuck が束縛されたとしたら、 先頭の2つのトークン br が比較されて常に \else または \fi まで読み飛ばされてしまいますよ。

\ast{brainfuck} の例に戻りましょう。 !b は「同じ」じゃないので、次の条件分岐は、

\ifx!b\else*\expandafter\recur\fi rainfuck!

else節のようなものに処理がわたってこんなふうな結果になるように思えるかもしれません。

*\expandafter\recur rainfuck!

しかし、実際にはこうなります。

*\recur rainfuck!

\expandafter はいったいどこにいってしまったんでしょう? 

実は \ifx のような条件分岐命令は、TeX が「展開」するトークンを条件に基づいて制御するものであり、「条件を満たさなかったら \else から \fi までの部分を実行する」ような代物ではありません。 この場合は、「条件を満たさなかったら \else までを読み飛ばす」だけです。 なので \ifx!b まで展開した TeX は、条件を満たさないので次の \else まで読み飛ばし、 *\expandafter\recur\fi rainfuck! の展開を試みることになります。

展開順の制御は \expandafter から

いまはトークンリストを展開しているので、最初に出てくる * は単なる文字ではなく、カテゴリコードという値がついたトークンです。 最終的にはアスタリスクが出力されるし、単なる文字との違いを意識しなければならないわけではないのですが、トークンリストに読み込まれているのは、あくまでも記号などの文字を意味するカテゴリコード12がついた文字トークンです。

その次にくるのが、\ifx によって消えてしまったかのように見える \expandafter です。これは「直後のトークンの展開を待たせる」という仕事をする TeX のプリミティブです。 ここに \expandafter があるため、 TeX はトークンリストで次にくる \recur のことは忘れて、 \fi を展開します。これは言うまでもなく、条件分岐が一つ終わりますという命令です。 なので、先に \ifx から始まった条件分岐は、この時点で無事に終わります。

ここで、先ほど \expandafter されていた \recur が元の位置に戻されます。 その後の rainfuck! は、全部文字トークンです。 結果として得られるトークンリストが *\recur rainfuck! というわけです。

というわけで、 \recur brainfuck!*\recur rainfuck! になりました。 ここで再び \recur が展開されて **\recur ainfuck! になり、さらに \recur が展開されて ***\recur infuck! になり、…… *********\recur ! まで同様に展開されていきます。 最後の ! は、 \recur に読まれると \ifx !! となり条件を満たすため、今度は \fi まで一気に読み飛ばされます。 その結果得られるトークンは何もないので、最終的に一連の文字トークン ********* が TeX の出力処理に回ることになります。 これが TeX プログラミングにおける末尾再帰です。まるでパズルですね。

さらに深く

この \ast には重大な制約があります。 まず明らかな制約として、再帰の基底ケースとして ! を使っていることから、 ! が含まれる文字には使えません。 さらに、文字列の中に対応するブレースで囲まれた部分があると、その全体が一つの * に変換されてしまうという問題があります ( \recur {foo}bar が何に展開されるかを上記と同じ要領で考えると、なぜだかわかりますよね)。

Note:ブレースで囲んで入力するとトークンリスト上に1つのトークンとしてまとめられるわけではないことに注意してください。このようなブレース区切りは、マクロ定義にあるパラメータ引数(#1 sなど)にトークンをマッチさせるときだけ意味をなします。 たとえば \ifx の条件チェックでブレース区切りが意味を持たないことは説明しましたが、さらに、たとえば \ifx ... \else ... \fi の中で左右のブレースが閉じていないようなマクロを書くことも可能です。 この性質は、実用的なマクロの多くでも実際に利用されています。

いちばん大きな制約というより欠陥は、この \ast マクロはスペースを文字として勘定してくれないことです。 TeX は、スペース(空白)を読み込むとき、暗黙に二通りの解釈をしてトークンリストを作ります。 空白のつもりでファイルに入力したのにトークンリストには何も詰め込まれなかったり、逆に無意識に入れていた何かが空白を表すトークンとしてトークンリストに詰め込まれたりするのです。 空白を意図通りにトークンリストに詰め込むためには、 TeX の文字解釈と展開とマクロのパラメータに対するさらなる理解が無駄に求められます。

そもそも、ここまで「文字列」とぶっちゃけて言ってきましたが、 TeX にそんなデータ型はありません。 ドキュメントを書くのに入力している文字たちは、あくまでもトークンリスト上のトークンを形成するものです。 それを意識しながら TeX ソースを読んだり書いたりすると、 TeX マクロってエンジニアリング的には微妙だけど仕組みとしては意外と面白いかもと感じてきます。 見た目のプログラミング言語っぽさに惑わされると不自由さにがっかりするかもしれませんが、トークンリストの展開順序をコントロールして遊ぶゲームだと思うとなかなか挑戦しがいがある時間泥棒だったりします。

最後に

!」の制約はおいといて(ほんとはおいといちゃだめだけど)、代入を使わずにブレースとスペースの問題に対処すると、「TeX 芸人二段」 という称号がもらえることになっています。 ひまなときにチャレンジしてみよう!

この記事は「Learn You A Haskell for Great Good!」というHaskellのチュートリアルのパロディーです。ほかの章はありません。TeXに関する説明には慎重を期したつもりですが、内容には執筆者の勘違いが含まれている可能性があるので、うそを見つけたらぜひコメントをください。

2013/12/18

colortbl で \cline を使いたい

日本でLaTeXが生きのこるには、縦横無尽に罫線が引かれた彩り豊かな表を描けなければならない。 でも、表のセルに色を塗るための colortbl パッケージと、横線を好きな場所に引くための標準的な方法である \cline には、罫線までもが色で塗りつぶされてしまうという困った問題があります。

\begin{tabular}
  {|>{\color{white}\columncolor{red}  }c
   |>{\color{white}\columncolor{green}}c
   |>{\color{white}\columncolor{blue} }c|} \hline
  a & b & c \\ \cline{1-1}\cline{3-3}
  1 &   & 3 \\ \hline
\end{tabular}

本当なら、こんな具合に、左右の列だけにセル間の横罫線が引かれてほしいわけです。

これは TeX & LaTeX Advent Calendar の18日目の記事です。 昨日はabenoriさんの「可換図式を書こう:tikz-cdを使う.」です。

公式見解

この \cline が上書きされてしまう現象は、 colortbl のマニュアルでも言及されている既知の問題です。 いわく、「これは \cline の仕様なので代わりに \hhline を使え」とあります。

10 Less fun with \cline
Lines produced by \cline are coloured if you use \arrayrulecolor but you may not notice as they are covered up by any colour pannels in the following row. This is a `feature' of \cline. If using this package you would probably better using the - rule type in a \hhline argument, rather than \cline.

実際、"TeX - LaTeX Stack Exchange" における質問でも、 \hhline を使った回答が寄せられているようです(参照1参照2)。 しかしぶっちゃけ \hhline はまったく解決策になりません。 それどころか、今度は罫線を引きたくない箇所に白い線が出現したり、縦罫線が切断されたりしてしまいます。

\usepackage{hhline}
...
\begin{tabular}
  {|>{\color{white}\columncolor{red}  }c
   |>{\color{white}\columncolor{green}}c
   |>{\color{white}\columncolor{blue} }c|} \hline
  a & b & c \\ \hhline{-|~|-}
  1 &   & 3 \\ \hline
\end{tabular}

これはひどい。

ごまかし方

colortbl で特定の列にだけ横罫線を引くことは不可能なのでしょうか。 実は、方法がないではないので、それを紹介します。ただし後述するように、厳密にいうとこの解法には落とし穴があるので注意してください。

そもそもなんで罫線が塗りつぶされるかというと、 colortbl では罫線を引くプロセスのあとにセルに色を塗っているからなのですが、これは TeX の仕組み的にはチートできません。 色を塗るにはセルの大きさが決定していなければならず、その段階では罫線も引き終わることになっているからです。

でも逆に考えれば、セルの高さに対して罫線の位置をはじめからずらして引くようにしてしまえば、罫線を微妙に避けて色を塗ってくれるはずです。 そこで次のような \cline の定義(もとは latex.ltx にあるけど colortbl パッケージで上書きされてるもの)を……

%% \@cine from colortbl.sty
\def\@cline#1-#2\@nil{%
  \omit
  \@multicnt#1%
  \advance\@multispan\m@ne
  \ifnum\@multicnt=\@ne\@firstofone{&\omit}\fi
  \@multicnt#2%
  \advance\@multicnt-#1%
  \advance\@multispan\@ne
  {\CT@arc@\leaders\hrule\@height\arrayrulewidth\hfill}%
  \cr
  \noalign{\vskip-\arrayrulewidth}}

次のように書き換えてプリアンブルに書きます(\makeatletter を忘れずに)。

\def\@cline#1-#2\@nil{%
  \noalign{\vskip-\arrayrulewidth}
  \omit
  \@multicnt#1%
  \advance\@multispan\m@ne
  \ifnum\@multicnt=\@ne\@firstofone{&\omit}\fi
  \@multicnt#2%
  \advance\@multicnt-#1%
  \advance\@multispan\@ne
  {\CT@arc@\leaders\hrule\@height\arrayrulewidth\hfill}%
  \cr}

元の定義で一番最後にあった \noalign{\vskip-\arrayrulewidth} を先頭にもってきただけで、ほかはそのままです。 これだけで、ふつうに \cline を使えば横罫線が出現するようになります。もちろん不要な箇所に白線が出ることもありません。

\begin{tabular}
  {|>{\color{white}\columncolor{red}  }c
   |>{\color{white}\columncolor{green}}c
   |>{\color{white}\columncolor{blue} }c|} \hline
  a & b & c \\ \cline{1-1}\cline{3-3}
  1 &   & 3 \\ \hline
\end{tabular}

やったね。

うまくないところ

この方法は、罫線を、罫線の幅だけ上にちょっとずらすことで、一見成功しているように見える結果を得ています。 なので、ずれてない本来の位置に罫線を一緒に引いてみると、アラがわかります。

\begin{tabular}
  {|>{\color{white}\columncolor{red}  }c
   |>{\color{white}\columncolor{green}}c
   |>{\color{white}\columncolor{blue} }c|} \hline
  a & b & c \\ \cline{1-1}\cline{3-3}\hilne
  1 &   & 3 \\ \hline
\end{tabular}

残念ですね。

2013/12/10

XML を変換するなら Haskell(HXT) で

思えば自分にとってプログラミングとは、XML っぽいドキュメントを操作することでした。 XML パーザを書いてみたりSXML でいろいろがんばったり、やる前から XSLT に挫折したり。 とにかく気持ちよく XML を操作する方法をこれまでいろいろ探してきたわけですが、今はこう断言できます。 XML をいじるのに最高な道具は HXT(Haskell XML Toolbox)であると。 そこで自分用のメモを兼ねて、「こんなふうに HXT を使ってます」を紹介しようと思います。 基本的な Haskell 力が高くないので、「理解を間違っている」とか「もっとこう使うべき」というツッコミ歓迎です。

HXT がうれしい理由

XML のような木構造のデータをいじろうと思ったとき、まず思いつくのは、根っこから順番に要素をたどりつつ処理していく方法です。 しかし、この方法は案外と融通が利きません。 常に部分木だけを見ればいい仕事なら気楽なのですが、親の要素に戻ったり、兄弟要素にアクセスしたりする必要があるときは、 「いま木全体のどこを処理してるか」という情報を持ち歩くしかないからです。 そして、「いま木全体のどこを処理してるか」を意識しながらコードを書くには、かなりの精神力が要求されます。

HXT では、これとはちょっと違う方法で XML の木構造をいじれます。 「木のこの部分を何かに変換」とか「木のここを取り出す」とか、そういう個別の処理を組み合わせることで全体の変換処理を書けるのです。 同様のアプローチをとるツールとしては XSLT がよく知られていますが、 HXT では各処理を「ふつうのプログラミング言語」である Haskell のコードとして書けるので、 はるかに柔軟だといえると思っています(なにせ XSLT に挫折しているので断言するほどの自信はない)。

組み合わせたい各処理は、「XML の木」から「XML の木」への関数だと思えます。それらをいろいろ組み合わせたいのだから、これは「XML の木構造、および、XML木→XML木な関数たち」から「XMLの木構造、および、XML木→XML木な関数たち」への対応だと思えます。というわけで、 HXT では変換処理を Haskell における圏の間の関手 >>> を使ってつなげたりできます。

とはいえ、 >>> だけだと条件分岐もできないし、大して面白い組み合わせが書けないので、 HXT ではもうちょっと広い概念である「アロー」を使って XML 木の変換を組み合わせられるようになっています。アローについて詳しくは『関数プログラミングの楽しみ』の第10章を読んでください(言うまでもなくこれは宣伝です)。

変換を組み合わせる前に

まずは変換処理が一つしかない場合から書いてみます。 <div class="chapter">...<div> という要素を <h1>...</h1> に包み直すという、単純な変換がやりたいとしましょう。 構造を変えるのではなく、属性に応じて要素名を書き換えるだけの処理です。

なにはともあれ、最初に HXT まわりのモジュールを読み込みます。

import Text.XML.HXT.Core
import Text.XML.HXT.Arrow.XmlArrow
import Control.Arrow

これから変換処理をアローとして書いていくわけですが、外界とのやり取りにもアローを使います。 ここでは、ファイルなどの URI を受け取って入力を行うアロー readDocument と、同じく出力を行うアロー writeDocument を使います。 それぞれ入出力の書式などを指定することもできて、 DTD の指定やインデントの有無を設定できるのですが、細かいことはドキュメントを参照してください。 さらに、 main の中でアローを実行するために、 runX という関数を使います。

readDocumentwriteDocument、 それにこれから書く予定の「divh1 に変換する」アローである chapterToH1 という三つのアローをすべて >>> でつないで一つのアローを作り、 それを runX で実行するだけの超単純な main は、こんな感じになります。

main :: IO ()
main = do
  runX (readDocument [] "test.html"
        >>>
        chapterToH1
        >>>
        writeDocument [] "result.html"
        )
  return ()

それでは、肝心の変換処理 chapterToH1 を書いてみましょう。やりたいことは、「もし divclass 属性に chapter を持っていたら要素名を h1 にして、 class 属性はいらないので消してしまう」です。これはそのまま、 XmlTree から XmlTree へのアロー ArrowXml として、こんな Haskell の関数で書けます。

chapterToH1 :: (ArrowXml a) => a XmlTree XmlTree
chapterToH1 =
    processBottomUp
    (ifA (hasName "div" >>> hasAttrValue "class" (=="chapter"))
             ((setElemName $ mkName "h1") >>> removeAttr "class")
             (this))

狙いの div がどこに出てくるかわからないので、 processBottomUp を使って木を再帰的に見るようにしています。 ifA は条件分岐のためのアローです。条件にマッチしない要素はそのまま残しておきたいので、 else に相当する部分では this アローを設定しています(もしマッチする要素だけ残したいなら none というアローを使います)。そのほかのアローの役割は関数名でわかりますね。こんな具合にアローをつなげて、目的の処理を表すアローを書くわけです。

Haskellのパワーを活用する

先ほどの例と同じ要領で「div 要素を class 属性に基づいて都合のいい名前の要素に変換する」アローをコピペで量産し、それら全部を >>> でつなげるだけでも、それなりに役に立つXML変換プログラムが書けそうですが、それはつらいので、できればアローを作る関数を一つだけ作って、それを使って作ったアローたちを >>> で fold できないものかなと考えますよねふつう。実際、アロー版のfoldといえる seqA が用意されているので、こんなふうに書けます。

(seqA . map (uncurry divClassToElem)
           $ [("chapter", "h1")
             ,("section", "h2")
             ,("para", "p")
             ])

これを runX の中に書けばいいわけです。 divClassToElem はこんなふうに定義すればいいでしょう。

divClassToElem :: (ArrowXml a) =>
                  String            -- if DIV is this class,
               -> String            -- turn that into this element.
               -> a XmlTree XmlTree
divClassToElem cls elm =
    processTopDown
    (ifA (hasName "div" >>> isClass cls)
             (tameClass elm)
             (this))

tameClass :: (ArrowXml a) => String -> a XmlTree XmlTree
tameClass elm = (setElemName $ mkName elm) >>>
                removeAttr "class"

isClass :: (ArrowXml a) => String -> a XmlTree XmlTree
isClass val = hasAttrValue "class" (==val)

兄弟をまとめる

もうちょっと現実的な例題として、いかにも組版ソフトが吐き出したっぽい HTML を、それなりに整った HTML に変換したいとします。つまり、こんなデータを……

<html>
<head></head>
<body>
<div class="chapter">XSLTを捨ててHXTを使おう</div>
<div class="para">HXTでXSLTの処理系さえ実装できる!</div>
<div class="section">Allowとは</div>
<div class="para">こまけーこたーどーでもいいんだよ。</div>
<div class="section">HXTの使い方</div>
<div class="bulletA"><span class="shell">cabal install hxt</span></div>
<div class="bulletB"><span class="haskell">import Text.XML.HXT.Core</span></div>
<div class="bulletB"><span class="haskell">let doc = readString [] text</span></div>
<div class="bulletC"><span class="haskell">runX doc</span></div>
</body>
</html>

こんなふうに変換したいとします。

<html>
  <head/>
  <body>
  <h1>XSLTを捨ててHXTを使おう</h1>
  <p>HXTでXSLTの処理系さえ実装できる!</p>
  <h2>Allowとは</h2>
  <p>こまけーこたーどーでもいいんだよ。</p>
  <h2>HXTの使い方</h2>
  <ul>
    <li>
      <code>cabal install hxt</code>
    </li>
    <li>
      <code>import Text.XML.HXT.Core</code>
    </li>
    <li>
      <code>let doc = readString [] text</code>
    </li>
    <li>
      <code>runX doc</code>
    </li>
  </ul>
  </body>
</html>

この例で面倒なのは、 class 属性の値だけに基づいて、元の XML で潰れてしまっている構造(この場合は ul 要素)を取り出さなければならないところです。 面倒とはいっても、リストみたいなデータ構造から「bulletA」~「bulletなんとか」までの連続する要素をとってくるような問題そのものは、Haskell であれば Data.ListgroupBy を使って割と単純に解けてしまいます。 特定の属性の値という条件のままで考えるとちょっと込み入ってしまうので、代わりに要素名がそれぞれ「bulletなんとか」になってると単純化して考えてみます。 HXT なら、要素名を属性の値に変換した木を作るアローを作って前段にかませばいいだけなので、こうみなしても後で困ることはありません。

import qualified Text.XML.HXT.DOM.XmlNode as XN
import Data.List

groupBullet :: [XmlTree] -> [XmlTree]
groupBullet ts = map bulletlines $ groupBy isBullet ts
  where bulletlines [x] = x
        bulletlines a@(x:xs) = XN.mkElement (mkName "ul") [] a

isBullet :: XmlTree -> XmlTree -> Bool
isBullet t1 t2 = case (XN.getElemName t1, XN.getElemName t2) of
  (Just x', Just y') -> let x = qualifiedName x'
                            y = qualifiedName y'
                        in    (isPrefixOf "bullet" x)
                           && (isPrefixOf "bullet" y)
                           && (not $ isPrefixOf "bulletA" y)
  (_, _) -> False

この groupBullet はアローではなく、 XML 木から XML 木への単なる関数です。あとでアローに持ち上げます。

その前に、「bulletなんとか」という class 属性を持つ要素の名前を、その属性の値に変換するアローを書いておきましょう。 これがないと、いま定義した groupBullet 関数を使っても意味がありません(だって、元の木では要素名が全部 div ですから)。

classValToName :: (ArrowXml a) => String -> a XmlTree XmlTree
classValToName cls = 
    setElemName $< ((isClassPrefixOf cls 
                     >>> getAttrValue "class" 
                     >>> arr mkName)
                    `orElse`
                    getElemName)

isClassPrefixOf :: (ArrowXml a) => String -> a XmlTree XmlTree
isClassPrefixOf val =     
    (hasAttrValue "class" (isPrefixOf val))

この classValToName というアローを使って、次のようなアローを mainrunX の中に仕込めば、つぶされていた構造を ul 要素として取り出せます。

processTopDown
(((getChildren >>> classValToName "bullet")
  >>. groupBullet)
 `when` (hasName "body"))

ここでポイントになるのが >>. という演算子です。 この >>. 演算子は、前段にあるアローの行先に対して、リストからリストへの関数を適用したアローを返します。 さきほど定義した groupBullet[XmlTree] -> [XmlTree] な関数だったので、 >>. でアローと接続することで、「bulletなんとか」をグループにして ul でくくるというアローになります。 なお、あからさまな条件を when で指定して全体をわざわざ囲っているのは、こうしないと groupBullet したい要素たちを取ってこれないからです。

最後に、 class 属性の値が「bulletなんとか」の div 要素をすべて li に変換しましょう。 基本は最初に書いた chapterToH1 とまったく同じですが、今度は条件分岐に choiceA アロー使い、上で定義した isClassPrefixOf を条件にしてこんなふうに書いてみました。

bulletToLi :: (ArrowXml a) => a XmlTree XmlTree
bulletToLi =
    choiceA [isClassPrefixOf "bullet" :-> (tameClass "li"),
             this :-> this]

この例の全体を gist に張っておきます。

まとめ

ここでは、ほとんど >>> を使うだけで済む例しか出てきませんでしたが、実際にはかなりたくさんのアローの組み合わせ方が HXT で提供されています。基本的な XML の操作に便利なアローも、あらかじめたくさん実装されています。さらに、 >>. のようなリストとの連携機能も豊富です。おかげで、 Haskell の関数としてなら容易に書けるような処理を、 XML 木の変換をするアローとしてそのまま使えるようになります。

今回の記事で最後に書いたような HTML データの簡単な変換処理は、編集の現場ではちょくちょく遭遇します。 実際、 InDesign のような DTP ソフトが吐き出す XML を整形するのに、 HXT を利用して同じようなスクリプトを書いています。 些細な原稿データを一回だけ変換するなら、エディタの正規表現による置換で済ませてしまうほうがお気楽ですが、もっと複雑だったり素性が知れなかったするXMLデータを繰り返しいじるなら、 HXT も選択肢にいれてあげてください。

というわけで、去年に引き続き、今年の Haskell Advent Calendar 2013 も「編集者のための Haskell 入門」をお届けしました。

2013/12/02

TUG 2013で日本語の巻末索引について発表しました

TUG 2013という、世界中からTeX関連の開発者と利用者が日本に集まるイベントで、日本語書籍の索引について発表する機会がありました。「発表してみないか」と実行委員の黒木さんに誘われたときは、仕事でやってることを10分くらいで紹介すればいいのかなと思って軽い気持ちで引き受けたのですが、実は「海外からの参加者向けに用意する日本語チュートリアルの一環なのでよろしく」という話でした。当日は早口でいろいろ詰め込んでしまったせいか、話の筋を見失った方もいると聞いたので(すみません)、いまさらですが発表のストーリーをまとめておきます。(TeX & LaTeX Advent Calendar 2013の2日目の記事です。1日目はzrさん、3日目はdoraTeXさん。)

  1. いろいろな言語の本の索引を比べてみると、索引項目(一般には単語)の「自然な」並べ方がある言語とない言語がある
    • アルファベットを使う言語(ラテンアルファベットだけでなくハングルも含む)や、広義のアルファベットを用いる言語(タイ語とか)では、文字は言語によっていろいろだけど、単語は字面だけを見て並べられる
    • 一方、アルファベットを持たない日本語と中国語では、単語の並べ方に工夫が必要
  2. そこで、日本語の本における索引の並べ方を概説
    • いわゆる50音順は、文字の順番じゃなく、シラブルの順番
    • 中国語の辞書などで採用されてるピンイン順との比較
    • 人名を並べるときなど、50音順を破って字面優先にする場合(いわゆる電話帳順)もあるよ
  3. さらに具体的に、LaTeXで日本語の索引をどうやって作ってるか
    • makeindexやxindyは使えないので、日本語専用のmendexというツールを使うしかない
    • 漢字の読み仮名を機械的に解決するには、辞書だけじゃなくて、わかち書きのために形態素解析器も必要
    • 原稿がマークアップで汚くなるのを回避するために、gitで索引用のブランチを作るといいよ
  4. いろいろ面倒だけど、索引重要
    • 索引は、本文から単語を探すツールであるだけじゃなくて、本文の圧縮版としての役割もあるんだよ
    • 本を書いたり編集したりする人は、どうせ書いたものを何度も読むことになるんだから、そのうちの一回を「索引を作る」にあてるべきだよ
    • 電子書籍には電子書籍の索引の形があるはずなので模索していくべき

発表後、主に欧米の方々から個別にいろいろ質問されました。

Q:索引項目は文字列として短いのに、それでも形態素解析器が必要なの?
A:日本語は、まず分かち書きをしないと辞書すらひけないから、そのためだけでも必要。

Q:バージョン管理には何を使ってるの?
A:git(実際にはgit-svnが多い)

Q:たとえば「山」の字は「さん」と「やま」と二通り読むらしいけど、索引ではどっちに載せるの?
A:どういう単語に「山」の字が含まれてるかによって、たいていは一通りに決まる。決まらない例として、「空リスト」は「からりすと」と「くうりすと」とが毎度議論になるけど、自分は両方載せちゃう。

Q:索引をちゃんと作ったほうがいいというお前の話はまったくそのとおりだ
A:ありがとうございます!

Q:xindyは文字の順番を定義できるけど、なんで日本語では使えないの?
A:設定ルールがアルファベット前提っぽいから、少なくとも何らかの工夫は必要そう。それ以上詳しいことは今はちょっとわからない。

Q:中国語の索引は画数って話だけど、同じ画数の漢字の順番はどう決まるの?(実際には質問されなかったけど、勝手に想定してた質問)
A:筆順で決まる。「横線」「縦線」「はらい」「点」「折れ」の順(らしい)。

英語で発表というのもあって、準備はものすごい大変だったけど、終わってみればとても面白かったです。声を掛けていただいた黒木さん、本当にありがとうございました。事前の練習にお付き合いいただいた関東在住のTeXユーザーの皆様にも本当にありがとうございました。

本音を言うと、「日本語チュートリアル」という4つしかない枠の1つをまるまる「索引」に割り当てるのは、はっきりいって頭がおかしい構成ではないかと思っていました。今でも思います。もしかしたら、日本で開催するTeXの国際会議の一コマとして、コンピュータによる日本語の「排列」(用語の標準的な並べ方)の話題に絞るほうがよかったのかなあとも思います。とはいえ、例えば上記のような質問は「(巻末)索引」という切り口だったからこそ出てくる問題なのだろうし、そもそも日本語の排列の一般論なんて自分にはお手上げだし、自分なりに全力は尽くしたと思いたい。

TUG 2013 雑感

内外のいろいろな方にお会いして、中古カメラの話とか子どもの話とか、TeX以外にもいろいろと盛り上がれてとても楽しかったです。

とくにフランク・ミッテルバッハさんがとてもかっこよく、すっかりファンになりました。LaTeXコンパニオンにサインもしてもらいました。自分の発表後に「お前の話はまったくそのとおりだ」といってくれたのも彼です(その前にプロジェクタの接続を手伝ってあげたのでリップサービスかもしれませんが気にしない)。これからはLaTeX3を使っていこうと思います。

Japanese LaTeXCompanion 1st ed. with autograph

2013/11/04

Personal Impression on TUG 2013

For me, TUG 2013 had started August 2012, when Yusuke Kuroki, a mastermind behind Japanese TeX users, asked me to describe indexes of Japanese books at the next TUG, which would be held in Tokyo. Having intended to apply for any presentation slot, over whatever topic, I agreed to his request. I didn't think that I'm a sort of authority on this topic, but in the context of TeX, I was sure that I had enough experiences on indexes.

So I undertook a brief tutorial on making back-of-the-book indexes using TeX. What I didn't expect was that the tutorial would be over an hour long. I wasn't sure if indexes in Japanese actually would be much interesting for the most of the attendees. In addition to that, I thought the whole tutorials seemed to be overly focused on the compositions and typesettings of Japanese, rather than TeX. Until the TUG 2013 actually started, I had been a little bit skeptical how much non-Japanese speakers would enjoy the whole tutorials.

My concerns proved to be unfounded. The art of formatting Japanese is likely to be realized by the non-Japanese TeX users, let alone by the developers of the various typesetting systems. It would be nice these tutorials let everyone think about what we need to avoid awful looking documents. To put it bluntly, that is one of the raison d'etre of TeX.

Of course, there's another reason why we use TeX. That is because TeX allows us to take advantage of computers in creating documents, unlike word processors which requires a bunch of hand work. And in this context too, there is some subtle point in regard to Japanese that we wanted to let the attendees know. I can't help but feel partially responsible for that. I think both my talk on arranging index entries and Kuroki-san's additional tutorial on input methods revealed the gimmicky way of handling Japanese in the computer and TeX.

As a matter of fact I'm not good at speeches even in my native tongue. I felt as if my stomach was yanked out of my mouth during my presentation. But finally I found myself enjoying that. It was the great experience to hand on my knowledge to the people, including legendary developers around the world. I was really excited when I was directly questioned by them after the presentation. Again I was upset, this time the other way round.

© Kenshi Muto

Anyway, I reckon TUG 2013 was the great international conference. I'd like to thank the organizers for their energetic effort, and my particular thanks to Kuroki-san for giving me a rare opportunity.

2012/12/30

2013年賀状

年賀状書いた。

%!
<< /PageSize [285 420] >> setpagedevice

% excerpt from Bill Casselman's 
% "Mathematical Illustrations - a manual of geometry and PostScript"
/ctransform { load
  1 dict begin
  /f exch def
  [{[3 1 roll f {moveto}]}
   {[3 1 roll f {lineto}]}
   {[7 1 roll
     f 6 2 roll
     f 6 2 roll
     f 6 2 roll
     {curveto}]}
   {[{closepath}]}
   pathforall
  ]
  newpath
  {aload pop exec} forall
  end
  } def

/f { % u = (x^2-y^2) / 200, v = 2xy / 100
  /y exch def
  /x exch def
  x dup mul y dup mul sub 150 div
  1.2 x mul y mul 100 div
} def

/fcn { % u = ((x-100)^2-y^2) / 7 , v = 1.2xy / 7
  /y exch def
  /x exch def
  x 100 sub dup mul y dup mul sub 28 div
  1.2 x mul y mul 7 div
} def

/setrandcolor {
  /m exch .4 add def
  /r1 {rand 1 mod 1 div m add} def
  /r2 {rand 3 mod 17 div m add} def
  /r3 {rand 10 mod 17 div m add} def
  r1 r2 r3 setrgbcolor
} def

/randcgrid {
  currentlinewidth 10 div setlinewidth
    0 .5 300 {
      /i exch def
      i 0 exch moveto 300 0 rlineto /f ctransform
      i 400 div setrandcolor stroke} for
    0 .5 300 {
      /i exch def
      i 0 moveto 0 300 rlineto /f ctransform
      i 300 div setrandcolor stroke} for
} def

/xoffset 200 def

0 1 20{
  /n exch def

  xoffset 10 add -10 translate randcgrid
  0 xoffset sub 20 translate
  0 0 moveto
  newpath
  /Titania findfont 11 scalefont setfont
  0.7 0.1 0.1 setrgbcolor
  12 5.5 moveto
  (2008 2009 2010 2011 2012 2013 2014) true charpath
  /fcn ctransform clip
  /fcn ctransform clip
  1 0.5 0.55 setrgbcolor
  fill
  stroke
  newpath
  1 0 0 setrgbcolor
  120 setlinewidth
  145 140 moveto
  145 400 lineto
  stroke

  initclip
  20 10 moveto
  /Georgia-Bold findfont 10 scalefont setfont
  0 0 0 setrgbcolor
  n =string cvs show
  (/20) show
  showpage
  0 0 moveto
} for

"Mathematical Illustrations"を読みつつパスを変換(実質的には等角写像でぐにゅっと曲げた)して生成した。/fcn が西暦の文字列を構成するパスを変換するための写像を表す関数で、/f が矩形の格子を背景のテクスチャへと変換する写像を表す関数。

SDIM0193

利用したフォントは Titania 。毎年、フォント探しに一番時間がかかってるような気がする。

毎年、親戚や家族付き合いのある友人向けに20枚くらいだけ印刷してて、今年のものは「2008年に生まれた息子も5歳を迎えます今年も来年も引き続きよろしくお願いします」と読みます。去年のものはだいぶ色きちがいになってしまったので、今年はあっさりとした配色にして、何か手書きするスペースを残しました。

2012/12/24

TeX で花火(手抜きバージョン)

ちょうど一年前、「LaTeX/TikZ で花火」というネタが流行ったのは記憶に新しいところです(参考1参考2)。 TikZ は高機能なグラフィック機能を提供する LaTeX のパッケージで、繰り返し構文なども提供されており、アルゴリズム的に花火っぽい絵を描画するコードがいくつか登場しました。

自分でも TeX & LaTeX Advent Calendar 2012 のために花火をひとつくらい打ち上げてみようかなと思ってたのですが、TikZ と格闘するような時間もなく、あきらめてかけていたところ、この花火ネタより前の2009年に話題を集めた LaTeX お絵かきネタを思い出しました。コーヒーポットのシミを論文などに刷り込むための LaTeX スタイル "LaTeX Coffee Stains" です。これの花火バージョンがあれば、なんとかゴール目前までつながった TeX & LaTeX Advent Calendar 2012 のお祝いネタとしてちょうどいいのではないでしょうか。

というわけで急遽でっちあげたのが、見た目にもめでたい「花火をページに刷り込むコマンド」です。花火を打ち上げたいページで \fireworks と書くだけ!

正直に打ち明けると、このネタは上記の coffee.sty の丸ぱくりです。 しかも自分では TeX のコードをまったく書いてません。元ネタである coffee.sty を見たら、PSTricks という PostScript の描画命令を直接埋め込める機能を使ってコーヒーのシミを描いていたので、この PostScript のシミデータを自作の PostScript 花火データにまるっと差し替えました。 PostScript の花火をどう作ったかは聞かないでください。 PSTricks では PostScript の描画コマンドは直接利用できるけど PostScript のプログラムを書けるわけではないので、アルゴリズム的には生成してないです。なお、 PSTricks は dvipdfmx では使えないので、PDFを作る場合は pdftex か dvips 経由で。

というわけで、明日の TeX & LaTeX Advent Calendar 2012 最終日はTeX芸人の中のTeX芸人、ZRさんの予定です。

2012/12/18

parsec で極める文章編集

正規表現をまったく使えない編集者はひとにぎりだと思いますが、正規表現だと原稿の半角丸括弧を全角に変換する作業とか頭痛いですよね。わたしもいつも困ってました。

というわけで、いまや編集者必須ツールといってもいい parsec を新人編集者にぜひ使ってもらおうということで、 「Haskell Advent Calendar 2012」18日目という場を借りた素人チュートリアル記事です。 Haskeller が書いてるわけではないので、 「その考え方は違う」とか「もっと効率的な書き方がある」といったコメントがもらえるとうれしいです。 ちなみに、わたしの周りに新人編集者はもう何年もいません。まだ見ぬ新人へ向けて書きます。

parsec で最速テキストフィルター

最初に parsec を使おうと思ったときにぶちあたるのは、プログラマ向けの解説しかないことだと思います。 編集者というものは、 CSV や IP アドレスをパースしたり、ましてや関数電卓を作ったりしない。 Ruby で正規表現を使ったテキストフィルターを書くときみたいに parsec を使うにはどうすればいいでしょうか。

外側から考えます。いま作りたいのは、入力ファイルを指定して、何か文字列変換を施した結果を出力するテキストフィルターです。Ruby なら ARGF.gets とかすればいいとこですが、ここは我慢してとりあえずこんな枠組みを書きます。

module Main () where

import System.Environment
import qualified System.IO as IO
import Text.ParserCombinators.Parsec hiding (many, (<|>))
import Control.Applicative

main = do
  args <- getArgs
  inh <- IO.openFile (args !! 0) IO.ReadMode
  body <- IO.hGetContents inh
  IO.putStr $ doSomething body
  IO.hClose inh

doSomething が具体的な変換処理で、ここに「日本語文章に出てくる半角丸括弧を全角に」とか「TeX の数式を抜き出す」とか「コード行に出てくるキーワードにハイライトのタグをつける」とかいった処理をするパーザを parsec で書くわけです。

では doSomething を考えましょう。ここでは doSomething という名前を説明のために使い続けますが、実際にスクリプトを書くときはテキスト変換処理を表す適切な名前をつけてください。

doSomething :: String -> String
doSomething lines = case parse (concat <$> manyTill block eof) "" lines of
  Left  err -> ""
  Right str -> str

テキストフィルターなので doSomething は文字列から文字列への変換を担う関数でないと困ります。 そこで 1行目には String -> String と書いてあります。

case ... of の内側の manyTill block eof が、とっかかりとなる最初のパーサです。 これは、「今はなんだか決めてないけど block という文字列の塊を取ってくるパーザがあるとして、それをファイルの終わり eof まで繰り返し実行する(manyTill)」、という意味です。 繰り返しとってきたその結果は、文字列のリストなので、一つにつなげるために concat <$> と書いています。 このように関数のうしろに <$> と書き、続けてパーザを書くと、「うしろに書いたパーザが返すものに最初の関数を適用したものを返すパーザ」になります。

ここで注意しないといけないのは、パーザは「文字列をパースして得られる文字列」を返してくれるわけではないという点です。 parsec では、パーズして得られる結果は「パーザを実行する専用世界」の中にあり続けます。 その専用世界を Parser と呼ぶことにしていて、 だから parsec におけるパーザは、たとえば文字列を返すものであれば Parser String といいます。文字を返すものなら Parser Char です。何も返さないパーザというのもあって(スペースを読み飛ばす、とかです)、これは Parser () といいます。

この Parser 世界の中にフィルタリングしたい文字列を入れて、パーズした結果をもらいたいわけですが、この世界とのやり取りは決められた出入り口からしかできないようになっています。 someParser というパーザを書いたとして、それで文字列をパーズして結果を「専用世界」の外に引っ張り出してくる方法のひとつが、上記の parse someParser "" lines という書き方です。

こうして手に入る結果は「パースに成功してこんな文字列が手に入った、または失敗した」というちょっと変わった形をしています。そのままでは doSomething の結果としてふさわしくありません(だって doSomething は文字列を返すってことにしたので)。そこで case ... ofRight および Left という識別子を使って、成功の場合も失敗の場合も文字列を返すようにしています。作ってるのがテキストフィルターなので、失敗の場合は空文字列を返しとけばいいでしょう。

ここでようやく block を何にするか決めます。 テキストフィルターの仕様を考えるわけです(というわけで、ここまではテキストフィルターを書くときの定型だと思ってもいいです)。

いま、HTML の <p> タグの内側にある半角丸括弧だけをすべて全角丸括弧に直したいとします。 一方、 <pre> タグの中にある半角丸括弧とかは、コードの断片である可能性が高いので、変換してはいけないとします。数式なんかに出てくる半角丸括弧も全角にしてはいけません。 いま仮に、 <p> タグの中には日本語の本文だけしかないものとしましょう(でないと説明のコードが増えてしまうからです。べつに一定のルールにしたがって出てくるぶんには、その部分だけ処理を飛ばすようにパーザを書けばいいのです。ただし完璧を目指すと泥沼になるので適当な精度で切り上げましょう)。

この場合の block の仕様はこうです。こいつは文字列を返すパーザにしたいので、 Parser String だと宣言しておきます。

block :: Parser String
block = choice [ try japara
               , otherlines ]

japara :: Parser String
japara = string "<p>" *> (conc <$> manyTill anyChar (try $ string "</p>"))
  where conc = ("<p>"++) . (++"</p>") . replaceParen

otherlines :: Parser String
otherlines = manyTill anyChar $ (try $ string "\n")

ざっくりというと、日本語の本文(japara パーサ)かそれ以外(otherlines パーサ)かで選択(choice 関数)をして、日本語の本文だったら半角括弧を全角に変換します(replaceParen 関数)。 block だけでなく、そこから呼んでる japara だったり otherlines だったりは、すべて文字列を返すパーザです。こんなふうに、基本的なパーザをいろいろ組み合わせて好きなパーザを作るわけです。 string [文字列] とか anyChar なんかも、もちろんパーザで、これらのいわば最小の部品は parsec にあらかじめ用意されています。ほかの部品はここにドキュメントがあるので探してください。

japara、つまり日本語の本文は、「文字列 <p> から </p> までの内側」です。 *> は、右側だけを結果に残すようなパーザを作ってくれます。

otherlines、つまり日本語の本文以外は、「改行までの文字なら何でも」とってくるパーザです。 この定義だと 1行とったら終わってしまうように思いますが、外枠のほうで block を何度も繰り返しとり出すことにしてあるので、これで問題ありません。

<p> タグ内で半角括弧の置換を行う replaceParen を書くには、 最初のほうで定義した doSomething と同じ考え方をします。 doSomething では本文全体から必要なブロックと不要なブロックを切り出すパーザを繰り返し使ったわけですが、今度は置換する要素としない要素に切り刻むパーザを作り、それで各ブロックを処理していきます。 丸括弧は入れ子になってるかもしれないので、丸括弧か否か(parensnoParens)だけでなく、丸括弧内か(inParens)も選択肢になりえます。

replaceParen :: String -> String
replaceParen line = case parse (concat <$> many1 strOrParen) "" line of
  Left err -> ""
  Right str -> str

strOrParen :: Parser String
strOrParen = choice [ try noParens, try inParen, parens ]

inParen :: Parser String
inParen = string "(" *> (wrapDP <$> (manyTill strOrParen (string ")")))
  where wrapDP = ("("++) . (++")") . concat

noParens :: Parser String
noParens = many1 $ noneOf "()"

parens :: Parser String
parens = many1 $ oneOf "()"

以上を ReplaceParen.hs のような名前で保存して以下のように実行すれば <p> タグの中だけ半角丸括弧を全角に置換できます。このブログ記事のソースみたいなのを処理しても、コード片に出てくる半角丸括弧は置換されません。やったね。

$ runghc ReplaceParen.hs input.html > result.html

parsec でテキストフィルターを書くときのまとめ

  1. 全体をブロック要素へと切り刻むパーザを choice で作り
  2. 各ブロックをインライン要素へと切り刻むパーザを choice で作る
  3. どちらも 「case parse [パーザ] "" [パーズする対象] of 」で文字列から文字列への関数にしたてる

書き捨てとはいえ、このような再帰的なパターンになると、正規表現をサポートしてるエディタではつらいし、sed/Ruby/Python/Gauche などでスクリプトを書くにしてもかえってコード力が要求されることが多いように思います。 単純よりちょっと込み入ったテキスト処理になると、Haskell のほうが parsec のおかげで楽に編集補助ツールが書けることもあるはずです。プログラマでないみなさんも『すごい Haskell たのしく学ぼう!』『プログラミング Haskell』だけは読んでおきましょう。再帰に対する理解もあるとなおよいので、『Scheme 手習い』もぜひ読みましょう。これは宣伝です。

Haskell のコードには、ここで出てきたような <$> とか *> のような記号がちょこちょこ出てくるのでとっつきにくいかも知れませんが、『すごい Haskell たのしく学ぼう!』の Kindle 版とか、ちゃんと索引もついてるので、こんな検索しにくそうな記号もばっちり調べられます。これは宣伝であると同時に、電子書籍にも索引あったほうがいいよという、この記事の対象者である新人編集者むけのアドバイスです。

注意

書き捨てのテキストフィルターに完璧は目指さないこと。あくまでも編集作業の補助に使いましょう。 実は上の例でも、たとえば原稿中に <p> 要素の入れ子がないことや、<p> タグがすべて行頭にあることを密かに仮定していて、だから otherlines が簡単に定義できてます。

経験からいうと、 otherlines のような「探してないその他大勢」をすっ飛ばすパーザを書くほうが大変で、行頭とか空白とか特殊文字といった都合のいい条件のない完璧なパーザを目指そうとすると、とたんにスクリプトが巨大になります。この例だと、それこそ XML パーザを書く勢いが必要です。しかし、いまほしいのは書き捨てのテキストフィルターです。妥協が肝心です。ビルドスクリプトに組み込むとかでなければ、一発で完全に処理しようとしないほうが幸せです。

2012/12/15

TeX がむかついたので実装したけど挫折してる話

いきなり私見ですが、「TeX むかつく」という声の大半は、「インストールが苦痛」または「マクロが意味不明で思い通りならない」に分類できるんじゃないでしょうか。 でもまあ、便利なインストーラ付属の分かりやすいLaTeX2eの入門書が改訂を重ねて書店で売ってたりするのだし、これらは普通の用途においては解決策がある問題ということにしておきましょう。

インストールの壁をクリアして、ちょっと凝ったページの出力もできるようになると、 基本的な組版機能に不足はないし、数式にいたってはほぼ唯一無二だし、自作マクロでちょっとした自動化さえできるわけで、 「とにかくTeXむかつく」という心境から、「いっけん意味不明な制約やなぞの挙動には目をつぶるとして TeX しかないな」という心境に至る人も多いはずです。 わたし自身も、組版に関しては、だいたいそういう心境です。残念ながら TeX しかない。

しかし、それでも TeX には実際むかつくことがある! それは、「TeX がドキュメントシステムに向いてない」ことです。「自由すぎる」ともいえます。

ドキュメントというのは、木構造になっているべきです。 キーワードやフォント指定用語などのインライン要素から構成される章、節、段落、図、数式といったブロック要素が、根っこで一つのドキュメントへと束ねられてるようなイメージです。 散文一般とドキュメントに区別があるとしたら、この木構造が必須か否かという点かもしれません。 ドキュメントは、なんらかの木構造と一対一対応がないと、最悪、読み手によって意味が変わってしまいます。 小説ならそれでいいんですが、たとえばジェット機の整備マニュアルなんかだと、それではまずい。 だから、まっとうなドキュメントには木構造が求められるわけです。XMLとかSGMLが重宝されるゆえんです。

だからドキュメントをシステマチックに扱うには、木構造を反映できるファイル形式が不可欠です。 さらに、ドキュメントを木として編集するための仕組みもあればうれしいでしょう。

TeX がドキュメントシステムに向いてないと思うのは、この木構造の観点が欠落しているからです。 もちろん、書き手が注意して執筆することで、 TeX を使って木構造のドキュメントを作ることはできます。 しかし、 TeX で執筆された原稿を扱う汎用の仕組みを作ろうとすると、構文解析すら無理ゲーなことに気づきます。 ドキュメントシステムの処理系を作ろうとしたら、原稿から木を作ってそれを内部表現として扱うと思うのですが、 TeX の場合、原稿を書くための基本的な構文も内臓のマクロ言語によって提供されているため、原稿から木を作りたいだけなのに、原理的にはこのマクロ言語の処理系を書かないと完全なものにならない。そこで実践では「サポートする TeX の構文」を決めうちすることで TeX マクロ言語処理系を実装する手間は省くことになってしまい、だから世の中には「汎用の LaTeX→XML コンバーター」のような仕組みが見当たらないのです。

ないから作った

ないなら作ればいいというわけで、TeXマクロ言語の処理系を Gauche で実装してみたことがありました。TeX-modoki といいます(その後「tef」に改名)。基本的なマクロの展開が扱えます。久しぶりにソース見たら \afterassignment\aftergroup も未実装だったけど、奇数個の \expandafter の塊くらいはまじめに処理できます。

tex-modoki$ cat sample/expandafter-test.tex
\def\A#1#2#3{#1#2#3a}\def\B#1#2{#1#2b}\def\C#1{#1c}\def\D{d}

\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter\A
\expandafter\expandafter\expandafter\B\expandafter\C\D

tex-modoki$ gosh -I. main.scm -o text sample/expandafter-test.tex
 dcba

トークンのカテゴリコードもまじめに扱ってるので、それ相応の判定結果が得られます。

tex-modoki$ cat sample/ifcat-test.tex
\catcode`[=13 \catcode`]=13
\def[{*}

\ifcat[* yes \else no \fi % yes

\ifcat\noexpand[\noexpand] yes \else no \fi % yes

\ifcat\noexpand[* yes \else no \fi % no

tex-modoki$ gosh -I. main.scm sample/ifcat-test.tex
 yes   yes  no

未実装の機能が多いので制限だらけですが、簡単な整数計算のマクロなら実行できるっぽいです。「スヤァTeX」に登場する、修正ユリウス通日を求めるマクロも、少し修正は必要ですが実行できました(できるように今朝ちょっと実装を追加した)。「\relax」の変わりに「( ˘ω˘ )スヤァ…」を使うには(ry

tex-modoki$ gosh -I. main.scm -o text sample/julian.tex
   56286

tex-modoki$ cat sample/julian.tex
\input sample/plain % tex-modoki 用に修正した plain TeX マクロを指定

\newcount\xx@cnta
\newcount\xx@cntb
\newcount\xx@year   % 4桁の整数の格納に mathchar を使わない!
\newcount\xx@month  % 2桁の整数の格納に char を使わない!

\def\julian#1{%     % LaTeXじゃないので newcommand を def に
  \edef\xx@tmpa{#1}%
  \expandafter\xx@julian@a\xx@tmpa///\relax
}
\def\xx@julian@a#1/#2/#3/#4\relax{%
  \xx@cnta=#1\relax
  \xx@cntb=#2\relax
  \ifnum\xx@cnta<1 \xx@cnta=\@ne \fi  % 条件式の \@ne を 1 に
  \ifnum\xx@cntb<1 \xx@cntb=\@ne \fi  % 条件式の \@ne を 1 に
  \ifnum\xx@cntb<3\relax
    \advance\xx@cntb by 12\relax
    \advance\xx@cnta by -1\relax
  \fi
  \xx@year=\xx@cnta
  \xx@month=\xx@cntb
  \multiply\xx@cnta by 1461\relax
  \divide\xx@cnta by 4\relax
  \xx@cntb=\xx@year
  \divide\xx@cntb by 100\relax  
  \advance\xx@cnta by -\xx@cntb
  \divide\xx@cntb by 4\relax
  \advance\xx@cnta by \xx@cntb
  \xx@cntb=\xx@month
  \advance\xx@cntb by -2\relax
  \multiply\xx@cntb by 520\relax
  \divide\xx@cntb by 17\relax
  \advance\xx@cnta by \xx@cntb
  \advance\xx@cnta by #3\relax
  \advance\xx@cnta by -678912
  \edef\thejulian{\number\xx@cnta}%
}

\julian{2012/12/25}
\thejulian

というわけでこの記事も TeX & LaTeX Advent Calendar 2012 の15日目に向けて書きました。12日ぶり2回目。

TeX の数式を HTML で出力

さらに副産物として、「素TeXで書いた数式の HTML 出力」ができます! もともと TeX の組版機能を実装するつもりはなく、その力もないのですが、TeX の数式を木構造へと変換できるところまでいったので HTML+CSS でも出力できるようにしてみました。

出力例はこんな感じで、残念なとこもあるけど、まあ見られるかなというレベル。

tex-modoki-1

ソースは、こちらのような、単純な TeX ファイルです。 しかし、 LaTeX はもとより、ごく基本的な TeX の標準マクロさえないので、手元にある LaTeX の数式を変換しても内部表現を吐き出すだけだと思います。

結局挫折

けっこうがんばってここまで作ってたのですが、今では開発をほぼ中止しています。なぜなら、TeX では字句解析時の文脈もマクロからいじれてしまう(ヘンタイ!)という事実に後から気が付いたので、完全なものにするにはゼロから作り直す必要があるからです。TeX むかつく!

数式変換機能の線でもうちょっとがんばって開発を続けようかなと思ったこともありましたが、 LaTeX や ams の膨大な遺産を前に挫折! やっぱり TeX むかつく!

2012/12/03

TeXを電卓として使おう(あるいは、TeXでベキ乗根)

プログラミング言語 TeX にはインタプリタがついてきます(ツッコミ禁止)。そこで他の多くの言語のインタプリタのように、TeX インタプリタを「電卓」として使ってみましょう。(なお、この記事は TeX & LaTeX Advent Calendar のために書いたものです。明日は @neruko3114 さん!)

まずは TeX インタプリタの起動です。日本語の組版をするなら ptex を実行するところですが、電卓に日本語はいらないので、今回は世界標準らしい pdftex を使います。 TeX インタプリタの標準プロンプトは * (アスタリスク)ですが、起動時だけは興奮気味に ** と2つ重ねて出力されるので、お約束として一言目は毎回 \relax と入力して TeX インタプリタを落ち着けてください。

$ pdftex
This is pdfTeXk, Version 3.141592-1.40.3 (Web2C 7.5.6)
 %&-line parsing enabled.
** \relax
*

さっそく 1+2 と入力してリターンを押したいところですが、TeX で単に数字や演算子を書いてしまうと、通常の文脈ではドキュメントへと印字する文字列とみなされます。いかにもドキュメント処理システムっぽいですね。数字を整数値として扱うには、整数用の \count レジスタというハコを用意してそこに値を格納し、そのレジスタ上で演算を行います。整数の加算からやってみましょう。

* \newcount\a
* \a=1
* \advance\a by 2
* \showthe\a
> 3.
<*> \showthe\a

?      % ここで単にリターン

*

1行めの \newcount\a で新しい \count レジスタ \a を用意し、2行めで\a に整数値の1を代入、 \advance\a by 2 という命令で \a 「2」の値に2を加算し、 \showthe というコマンドで \a の中身を出力しています。結果は赤字の「3」です。 \showthe で結果を表示したあとはプロンプトが ? に変わりますが、気にせずリターンを押せば元の * に戻ります。簡単ですね。なお \advance コマンドの引く数にマイナス記号をつければ(\advance\a by -2)、減算ができます。減算専用のプリミティブは用意されていません。

続いて、整数の乗算と除算です。それぞれ \multiply\divide という命令が用意されています。先程のセッションに続けて以下のように入力してみましょう(したがって、いま \a には 3 が入ってます)。

* \multiply\a by 3
* \showthe\a
> 9.
<*> \showthe\a

?      % ここで単にリターン

* \divide\a by 2
* \showthe\a
> 4.

最後の行は「9÷2」の結果なので「4.5」になるはずですが、 \count は整数用のハコなので端数は切り捨てられます。キャストとかされません。というか、そもそも実数そのものを扱うハコがありません。

では、結果が実数になるような複雑な計算は実現できないのでしょうか。試しにベキ乗根のつもりで \sqrt\a とかしてみましょう。

* \sqrt\a
! Missing $ inserted.

$がない、つまり数式組版モードじゃないと言われました! 当たり前と言えば当たり前なのですが、TeX というのは本来はドキュメントシステムなので、 sqrt はもちろん、 + や * 、 さらには数字にいたるまで、ふつうの言語なら数値やその演算を提供するであろう要素はほとんど組版の機能に利用されてしまっています。

しかし TeX は、「かっこいい出力ができる汎用プログラミング言語」でもあるはずです。ベキ乗根の近似値くらい計算できないはずがない。そこで、ベキ乗根の近似値をかっこよく組版で使うための TeX マクロ \sqrtd を作ってみましょう。目標は、こんな入力から、

You can get \sqrtd{2}, \sqrtd{2.5}, \sqrtd{3} and \sqrtd{3.14} 
by Newton's method with initial guess $1.0$.

こんな出力を得ることです。

newtonmethod-output

(2のベキ乗根があまり精度よくないのは目をつぶってください><)

TeXでニュートン法を実装しよう!

ベキ乗根の近似値を得る方法として一番簡単なのはニュートン法でしょう。まずはニュートン法を使って値 x のベキ乗根を求める手順のおさらいです。

  1. ベキ乗根になりそうな値 y を適当に推測します。
  2. その推測値 y のベキ乗 y2 と、元の値 x との差を調べます。
    • この差が十分に小さかったら、その推測値 y はだいたいベキ乗根に近いということで、答は y です。
    • この差が十分に小さくなかったら、推測値 y と x/y の真ん中の値をあらためて推測値 y とし、1.に戻ります。

いきなり TeX はきついので、とりあえず Scheme で書いてみます(詳しくはSICPの1.1.7節を参照)。

(define (sqrt-iter guess x)
  (if (good-enough? guess x)
      guess
      (sqrt-iter (improve guess x)
                 x)))

(define (good-enough? guess x)
  (< (abs (- (square guess) x)) 0.001))

(define (improve guess x)
  (/ (+ guess (/ x guess))) 2))

このアルゴリズムを TeX で実装したいわけですが、 TeX にはそのものずばりな実数値がありません。ただ、TeX本来の機能である自動組版を制御するために、値が実数になる「寸法(dimension)」というデータ型は用意されています。このデータ型上にも、整数値のときと同じ演算命令(\advance\multiply\divide)が制限付きながら用意されているので、そこに上記のアルゴリズムの x や y を格納すれば原理的にはベキ乗根が求められるだろう、という算段です。

ここで厄介なのが、寸法上の演算の制限です。寸法の値を自由に掛け算したり割り算したりすることはできません。寸法に対して可能な乗算と除算は、具体的には以下のとおり。

  1. 寸法は、整数倍したり、整数で割ったりすることはできる。
  2. 寸法の前に、何か数字っぽいものを書くと、その数字の値が寸法の値に掛けられる。

上記の 1. は、寸法の値に実数を掛けたり割ったりすることは基本的にできない、つまり、実数どうしの乗算と除算が寸法型のままではできないということです。

上記の 2. の手段を使えば、寸法に実数を掛けることはできます。ここでちょっと実際の TeX コードを見てみましょう。寸法データ型を用意して「3.14 ポイント」を格納し、その寸法を 1.5倍するには、例えばこう書きます。

\newdimen\x      % 新しい寸法データ\xを用意
\x=3.14 pt       % 寸法\xに単位付きの実数を格納
\x=1.5\x         % 寸法\xを1.5倍

寸法の単位はいくつかありますが、ここではポイント(pt)を使っています。2行めでポイントの単位「pt」を省略して 3.14 という実数値を寸法に代入しようとしてはいけません(TeX に苦情を言われます)。3行めの結果、 \x には「4.71 ポイント」に相当する値が入ります。

ここで、実数に実数を掛けようとして次のように書いたらどうなるか考えてみましょう。

\newdimen\x      % 新しい寸法データ\xを用意
\newdimen\y      % 新しい寸法データ\yを用意
\x=3.14 pt       % 寸法\xに単位付きの実数を格納
\y=1.5 pt        % 寸法\yに単位付きの実数を格納
\x=\number\y\x   % 寸法\xを1.5倍?

最後の行に出てくる \number は、直後のデータ型の数字表現を返すコマンドだと考えてください。上記を TeX に渡すと「Dimension too large.」というエラーになり、 \x16383.99998pt という寸法になるはずです。これは、 TeX の寸法の上限(をポイント単位で表したもの)です。

何が起こったのでしょう。\number\y は 1.5 ではなく、「ポイントで表すには "too large" だ」と TeX に言われてしまう何か巨大な寸法であり、TeX はとりあえずの処置としてポイント表示で最大な数字を \x に入れたのです。ちなみにその巨大な寸法とは、 1.5 ポイントを sp という単位で表した数字に 3.14 ポイントを掛けた寸法であり、308674.56pt です。実をいうと、TeX は内部では sp という単位の整数倍で寸法を扱っています。だから、寸法で実数が扱えるというのも実はまやかしで、ポイントくらい大雑把な単位で見れば小数表示になる寸法を実数とみなして扱える、ということだったりします。

というわけで、寸法そのものは単位付きの値なので、その単位で表した場合の実数値を示す数字として使うには、何とかして単位の部分をひっぺがす必要があります。そのための仕組みがこちらの \strippt です。

\newdimen\zero \zero=0pt
\begingroup
  \catcode`P=12
  \catcode`T=12
  \lowercase{
    \def\x{\def\rempt##1.##2PT{##1\ifnum##2>\zero.##2\fi}}}
  \expandafter\endgroup\x

\def\strippt{\expandafter\rempt\the}

何を言ってるのか分からないと思うが、私もこんなのゼロから書けるわけありません。これは LaTeX のコアで定義されているマクロをほぼそのまま拝借してきたものです。 \expandafter のとこだけ説明しておくと、ポイントを表す「pt」の各文字をちょっと特殊な文字とみなす閉じた環境で「なんとか.なんとかpt」というトークン列にマッチさせて \rempt というマクロを定義したいのだけど、それを外から使えるようにするために環境を閉じる \endgroup の展開を遅らせているのが最初の \expandafter で、その\remptを「なんとか.なんとかpt」という形の引数に対して使うべく \the で先に展開するために配置されているのが 2つめの \expandafter です。

以上の下準備でようやく、ニュートン法を実装するのに必要なベキ乗のためのマクロ \square が定義できるようになりました。

\newdimen\tempdimen \tempdimen=\zero

\def\square#1{\tempdimen=#1%
  #1=\strippt \tempdimen \tempdimen
  \tempdimen=\zero}

この \square を使ってニュートン法のアルゴリズム全体を書き下してみます。

\newdimen\prevguess
\newdimen\val
\newdimen\guess

\def\sqrtiter{%
  \prevguess=\guess
  \square\prevguess
  \advance\prevguess by -\val  % 推測値のベキ乗と元の値との差
  \ifdim\zero>\prevguess       % 差の絶対値
    \prevguess=-\prevguess\fi
  \ifdim\prevguess<0.001pt     % 差が十分小さかったら、
    \let\next=\relax           % おしまい。
  \else                        % 十分小さくなかったら、
    \improve\guess\val         % 推測値を改善して、
    \let\next=\sqrtiter        % 再帰。
  \fi\next}

上記でまだ未定義なのは、ニュートン法のアルゴリズムで推測値を改善させる部分を担うマクロ \improve です。次はこれを実装します。\improve には「実数÷実数」の演算がでてきますが、寸法の値を使った除算には直接の手段はなさそうなので、やや面倒な工夫が必要です。全体はこんな定義になりました。

\newcount\tempcountA
\newcount\tempcountB
\newdimen\tempdimenA
\newdimen\tempdimenB

\def\improve#1#2{%
  \tempcountA=#2%                   % いったん割られる数を整数にして、
  \multiply\tempcountA by 1000%     % 十分な倍率で大きくしておく。
  \tempcountB=#1%                   % 割る数も整数にする。
  \tempdimenB=#1%
  \divide\tempcountA by \tempcountB%% ここで割り算。
  \tempdimenA=\tempcountA pt%       % 結果をポイント単位で寸法に戻す。
  \divide\tempdimenA by 1000%       % 倍率を戻す。
  \advance\tempdimenA by \tempdimenB% 
  \divide\tempdimenA by 2%
  #1=\tempdimenA}

\newcount は、整数を格納できるハコを作ります。そこに「寸法」を代入すると、先に説明したsp単位の整数に変換されて格納されます。整数どうしは \divide というプリミティブで割り算できるので、これだけでもよさそうに思えますが、 \divide は剰余を切り捨てて結果を整数に丸めてしまうので、これだけだと結果の誤差が大きくなりすぎます。そこで、割られる数だけ十分な倍率を掛けておいて、割り算して結果を寸法に変換し直したあとで倍率を戻しました。

\improve の実装に必要な残りの演算は、単純な足し算と整数2による除算なので、特にこれ以上の工夫はいりません。これで寸法 \val\guess にそれぞれベキ乗根を求めたい数と初期推測値を入れておいて \sqrtiter を起動すると、ベキ乗根の値にポイントの単位がついたものが \guess に代入されます。あとは、こんなコマンドを定義すれば目標達成です。

\def\sqrtd#1{\val=#1 pt%
  \guess=1.0pt%
  \sqrtiter%
  $\sqrt{\number#1} \approx \strippt \guess$%
}

You can get \sqrtd{2}, \sqrtd{2.5}, \sqrtd{3} and \sqrtd{3.14} 
by Newton's method with initial guess $1.0$!

今回書いた TeX スクリプトの全体はこちら。なお、 LaTeX で実数を本気で使いたいなら、こんな面倒なことをしなくても、fpパッケージという便利な浮動小数点数演算のための仕組みがあります。そちらを使いましょう。