2012/12/01 〜 2012/12/25TeX & LaTeX Advent Calendar
こちらは平常(ry
* * *
(前回の続き)
TeX で「ガウス素数の模様」してみた
このプログラムで gptexture()
(\gptexture
)の引数に 39 を指定した場合、絶対値 39 以下のガウス整数が「模様の描画」の対象になるので、全体の模様は (−39,−39) から (+39,+39) の範囲に収まることになる。元の Lua のプログラムではこれを 79 行 79 桁のテキストで表現しているが、TeX では次のような図として出力することにする。
\begin{picture}(79,79)(-39,-39) %...... % 1-4i がガウス素数であるなら次の命令を実行 \put(1,-4){\rule{\unitlength}{\unitlength}} %...... \end{picture}
ここで図の描画で用いる \unitlength
の値を \gptunitlength
という寸法値マクロ(\renewcomand
で設定する)で設定できるようにした。
gptexture1.lua の set()
、reset()
、cur()
にあたるマクロは次のように成る。
\def\tcgp@set#1#2{\@namedef{tcgp@a/\number#1/\number#2}{1}} \def\tcgp@reset#1#2{\@namedef{tcgp@a/\number#1/\number#2}{0}} \def\tcgp@cur#1#2{\@nameuse{tcgp@a/\number#1/\number#2}}
\tcgp@set
と \tcgp@reset
は通常通りの書き換えになっている((ここでは名前参照のマクロに代入される値が定数(0 や 1)なので「nameedef」は不要で \@namedef
で済む。))が、\tcgp@cur
は少し異様である。cur()
は値を返す関数であるから、本則に従うと「手続きへの変換」が必要になるところであろう。ここでは \tcgp@cur
を(Lua や C 言語の)「関数」ではなく、単純に TeX の本来の意味での「マクロ」である(つまり単純に文字列の置き換えをするもの)と捉えてほしい。つまり、\tcgp@cur
は \@nameuse{...}
に展開されるマクロであるから、TeX プログラム中の「値」を表すコードとして \@nameuse{...}
があるなら、その代わりに \tcgp@cur
を書いてもよいはずである。((「マクロ」にならない「関数」との違いについて少し補足する。例えば、\def\ansA{42}
というマクロがあると、コード中の「42
」という記述を \ansA
に置き換えることができる(かも知れない)。これに対して、\def\ansB{\count@=42 \the\count@}
のようなマクロは確かに実行すると \ansA
と同じく「42」という出力を行うが、これはコード中の「42
」という記述の代わりにはならない。ただ実際にはこの推論についても「値を表すコード」(= 必要に応じてマクロが展開される文脈)という留保条件がつく。))
同様な考えで、絶対値(math.abs()
)を表すコード(前回に紹介した)を「マクロ」\tcgp@abs
にすることができる。
\def\tcgp@abs#1{\ifnum#1<0 -\fi #1}
すなわち、\tcgp@abs\tcgp@x
は「\ifnum\tcgp@x<0 -\fi \tcgp@x
」に展開される。
最後に not の扱いの話をする。if 文であれば、not を処理するのは容易で、単に then と else の部分を入れ替えればよい。
if not (a == 0) then b = a end ↓ \ifnum \tcgp@a=0 \else \tcgp@b=\tcgp@a \fi
これに対して、while 文(\@whilenum
)の場合はそのような方法が通用せず、従って、「元の式の否定と同値になる数値比較の式」を何とかして捻り出す必要がある。ここで TeX の if 文が三項演算子((Lua のイディオムでは判り難いのでここでは敢えて C 言語の書き方「(cond ? yes : no)
」を採った。))として使えることを思い出すと、「数値比較の結果の真偽値を一度 0/1 の整数に変換してから 0 と比較する」ことで所望の式が得られる。
while not (a == 0) do ... end ↓ while ((a == 0) ? 1 : 0) == 0 do ... end ↓ \@whilenum{\ifnum\tcgp@a=0 1\else0 \fi =0 }\do{ ... }
この定型のコードもマクロ \tcgp@not
として定義しよう。((実はこの定義だと #1
の直後の空白文字トークンの扱いが微妙で、例えば \tcgp@{0=\tcgp@a}
のように用いると「前の数値」によって吸収されず残ってしまう。ところが \ifnum...\fi
の部分自体が比較式の左辺の数値の部分に相当しているので、「1
」という数字を読む前にある空白トークンは結局無視されることになる。))
\def\tcgp@not#1{\ifnum#1 1 \else 0 \fi =0 } % これで \@whilenum{\tcgp@not{\tcgp@a=0}}\do{ ... }
これで gptexture1.lua を TeX プログラムに書き直す準備が整った。
完成したパッケージのコードは以下の通り。パッケージ名は tcgptexture、名前空間は tcgp。公開命令は \gptunitlength
と \gptexture
の 2 つである。
% tcgptexture.sty \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{tcgptexture} %% 変数定義 %(整数値) \newcount\tcgp@t \newcount\tcgp@n \newcount\tcgp@rn \newcount\tcgp@x \newcount\tcgp@y \newcount\tcgp@px \newcount\tcgp@py \newcount\tcgp@qx \newcount\tcgp@qy %(配列型) %\tcgp@ct/* %%(公開) \gptunitlength{<寸法>} % 1つの点(四角)の大きさ. \newcommand*{\gptunitlength}{2.5pt} %%(公開) \gptexture{<>} % 絶対値がn以下の範囲での模様を出力する. \newcommand*{\gptexture}[1]{% \begingroup \setlength{\unitlength}{\gptunitlength}% \tcgp@n=#1 \ifnum\tcgp@n<1 \tcgp@n=1 \fi \tcgp@gpt@setrn \tcgp@t=\tcgp@n \multiply\tcgp@t2 \advance\tcgp@t1 \begin{picture}(\tcgp@t,\tcgp@t)(-\tcgp@n,-\tcgp@n)% \tcgp@gpt@init \tcgp@gpt@sieve \tcgp@gpt@show \end{picture} \endgroup } %% 補助的なマクロ \def\tcgp@set#1#2{\@namedef{tcgp@a/\number#1/\number#2}{1}} \def\tcgp@reset#1#2{\@namedef{tcgp@a/\number#1/\number#2}{0}} \def\tcgp@cur#1#2{\@nameuse{tcgp@a/\number#1/\number#2}} \def\tcgp@abs#1{\ifnum#1<0 -\fi #1} \def\tcgp@not#1{\ifnum#1 1 \else 0 \fi =0 } %% \tcgp@pixel \def\tcgp@pixel#1#2{% \put(#1,#2){\rule{\unitlength}{\unitlength}}% } %% \tcgp@gpt@setrn \def\tcgp@gpt@setrn{% \tcgp@rn=0 \tcgp@t=0 \@whilenum\tcgp@not{\tcgp@t>\tcgp@n}\do{% \advance\tcgp@rn1 \tcgp@t=\tcgp@rn \multiply\tcgp@t\tcgp@rn }% \advance\tcgp@rn-1 } %% \tcgp@gpt@init \def\tcgp@gpt@init{% \tcgp@x=-1 \@whilenum \tcgp@x<\tcgp@n \do{% \advance\tcgp@x1 \tcgp@y=-1 \@whilenum \tcgp@y<\tcgp@n \do{% \advance\tcgp@y1 \tcgp@set{\tcgp@x}{\tcgp@y}% }% }% \tcgp@reset{0}{0}\tcgp@reset{1}{0}\tcgp@reset{0}{1}% } %% \tcgp@gpt@sieve \def\tcgp@gpt@sieve{% \tcgp@x=0 \@whilenum \tcgp@x<\tcgp@rn \do{% \advance\tcgp@x1 \tcgp@y=-1 \@whilenum \tcgp@y<\tcgp@x \do{% \advance\tcgp@y1 \ifnum \tcgp@cur{\tcgp@x}{\tcgp@y}>0 \tcgp@px=\tcgp@x \tcgp@py=\tcgp@y \@whilenum\tcgp@not{\tcgp@px>\tcgp@n}\do{% \tcgp@qx=\tcgp@px \tcgp@qy=\tcgp@py \tcgp@t=1 \@whilenum \tcgp@t>0 \do{% \tcgp@reset{\tcgp@qx}{\tcgp@qy}\tcgp@reset{\tcgp@qy}{\tcgp@qx}% \advance\tcgp@qx-\tcgp@y \advance\tcgp@qy\tcgp@x \ifnum \tcgp@qx<0 \tcgp@t=0 \fi \ifnum \tcgp@qy>\tcgp@n \tcgp@t=0 \fi }% \tcgp@qx=\tcgp@px \tcgp@qy=\tcgp@py \tcgp@t=1 \@whilenum \tcgp@t>0 \do{% \tcgp@reset{\tcgp@qx}{\tcgp@qy}\tcgp@reset{\tcgp@qy}{\tcgp@qx}% \advance\tcgp@qx\tcgp@y \advance\tcgp@qy-\tcgp@x \ifnum \tcgp@qx>\tcgp@n \tcgp@t=0 \fi \ifnum \tcgp@qy<0 \tcgp@t=0 \fi }% \advance\tcgp@px\tcgp@x \advance\tcgp@py\tcgp@y }% \tcgp@set{\tcgp@x}{\tcgp@y}\tcgp@set{\tcgp@y}{\tcgp@x}% \fi }% }% } %% \tcgp@gpt@show \def\tcgp@gpt@show{% \tcgp@x=-\tcgp@n \advance\tcgp@x-1 \@whilenum \tcgp@x<\tcgp@n \do{% \advance\tcgp@x1 \tcgp@y=-\tcgp@n \advance\tcgp@y-1 \@whilenum \tcgp@y<\tcgp@n \do{% \advance\tcgp@y1 \tcgp@px=\tcgp@abs\tcgp@x \tcgp@py=\tcgp@abs\tcgp@y \ifnum \tcgp@cur{\tcgp@px}{\tcgp@py}>0 \tcgp@qx=\tcgp@x \multiply\tcgp@qx\tcgp@x \tcgp@qy=\tcgp@y \multiply\tcgp@qy\tcgp@y \advance\tcgp@qx\tcgp@qy \tcgp@qy=\tcgp@n \multiply\tcgp@qy\tcgp@n \ifnum\tcgp@not{\tcgp@qx>\tcgp@qy}% \tcgp@pixel{\tcgp@x}{\tcgp@y}% \fi \fi }% }% } \endinput
テスト用文書は以下の通り。
\documentclass[a4paper]{article} \usepackage[scale=0.9]{geometry} \usepackage{tcgptexture} \begin{document} \begin{center} \gptexture{99} \end{center} \end{document}
ここまでの内容をマスターしたら、もう「TeX でマンデルブロ集合」も怖くない!?