Ackermann関数を計算過程を込めて出力するやつのTeX版を展開できるようにしたやつ.$\mathrm{Ack}$をAckermann関数とする.
\Ack{n}{m}
を展開すると$\mathrm{Ack}(n,m)$の計算過程が出てくる.各々の段階は\AckOutput
でくるまれる.例えば$\mathrm{Ack}(1,0) = \mathrm{Ack}(0,1) = 2$に対応して,\Ack{1}{0}
を展開すると\AckOutput{A(1,0)}\AckOutput{A(0,1)}\AckOutput{2}
となる.- e-TeX拡張は使う.といっても使うのは
\numexpr
のみである.+1と-1はこれを使わないと面倒…….
\documentclass[a4paper]{article}
\makeatletter
% メイン.内部表現に変換し,\ac@nextでループに移行.
\def\Ack#1#2{\ac@next{\ac@func{\ac@num{#1}}{\ac@num{#2}}}}
% 自然数nは\ac@num{n}で表す
\def\ac@num{\ac@num}
% Ack(X,Y)は\ac@func{X}{Y}で表す
\def\ac@func{\ac@func}
% \ac@num{n} -> n
\def\ac@numtonum#1{\expandafter\@firstofone\@gobble#1}
% \ac@ifnumeq{\ac@num{n}}{m}{true code}{false code}
% n = mならばtrue code,そうでなければfalse codeに展開される.
\def\ac@ifnumeq#1#2{%
\ifnum\ac@numtonum{#1}=#2 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
% uniq token
\def\ac@endmark{\ac@endmark}
\def\ac@eattoendmark#1\ac@endmark{}
% \ac@ifnum{#1}{true code}{false code}
% #1 = \ac@num{n}ならばtrue code,そうでなければfalse codeに展開される.
\def\ac@ifnum#1{%
\expandafter\ac@eattoendmark\ifx\ac@num#1\ac@endmark\expandafter\@firstoftwo\else\ac@endmark\expandafter\@secondoftwo\fi
}
% ループを進める.
\def\ac@next#1{%
\ac@output{#1}% 出力
% 自然数なら終わり,そうでなければ#1の計算を一段進める.#1=\ac@func{X}{Y}の形
\ac@ifnum{#1}{}{\ac@eval@once#1\ac@endmark}%
}
% 関数の計算を一段進める.#1=\ac@func, #2, #3は引数
% Ackermann関数の定義から#2は常に自然数である.
\def\ac@eval@once#1#2#3{%
\ac@ifnum{#3}{%
\ac@ifnumeq{#2}{0}{% A(0,#3) = #3 + 1
\expandafter\ac@eval@end\expandafter{\expandafter\ac@num\expandafter{\the\numexpr\ac@numtonum{#3} + 1\relax}}%
}{%
\ac@ifnumeq{#3}{0}{% A(#2,0) = A(#2 - 1,1)
\expandafter\ac@eval@end\expandafter{\expandafter\ac@func\expandafter{\expandafter\ac@num\expandafter{\the\numexpr\ac@numtonum{#2} - 1\relax}}{\ac@num{1}}}%
}{%
\ac@eval@othercase{#2}{#3}% その他
}%
}%
}{%
% 引数の順番を入れ替える.最終的に
% A(X,A(Y,A(....A(Z,A(V,W))..)
% が
% \ac@evel@end{A(V,W)の計算結果}{Z}...{Y}{X}
% に置き換わる.
\ac@eval@once#3{#2}%
}%
}
% {\ac@num{m}}{\ac@num{n}} -> @i{m - 1}{\ac@num{m}}{\ac@num{n}}
\def\ac@eval@othercase#1#2{%
\expandafter\ac@eval@othercase@i\expandafter{\the\numexpr\ac@numtonum{#1} - 1\relax}{#1}{#2}%
}
% {m - 1}{\ac@num{m}}{\ac@num{n}} -> @ii{n - 1}}{m - 1}{\ac@num{m}}
\def\ac@eval@othercase@i#1#2#3{%
\expandafter\ac@eval@othercase@ii\expandafter{\the\numexpr\ac@numtonum{#3} - 1\relax}{#1}{#2}%
}
% {n - 1}{m - 1}{\ac@num{m}}
\def\ac@eval@othercase@ii#1#2#3{%
\ac@eval@end{\ac@func{\ac@num{#2}}{\ac@func{#3}{\ac@num{#1}}}}%
}
% {A(V,W)の計算結果}{Z}...{Y}{X}\ac@endmarkをA(X,A(Y,A(....A(Z,A(V,W)の計算結果)..)に戻し,\ac@nextに回す
\def\ac@eval@end#1#2{%
\ifx#2\ac@endmark\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi{%
\ac@next{#1}%
}{%
\ac@eval@end{\ac@func{#2}{#1}}%
}%
}
% 出力ルーチン.\ac@func{#1}{#2}をA(#1,#2)に,\ac@num{#1}を#1にして,最後に\AckOutputでくるむ
\def\ac@output#1{%
\expandafter\AckOutput\expandafter{\romannumeral-`0\ac@output@i{#1}}%
}
% #1内をすべて変換する.
\def\ac@output@i#1{%
% 自然数ならばそのまま出力
\ac@ifnum{#1}{\the\numexpr\ac@numtonum{#1}\relax}{\ac@output@ii#1}%
}
% 第二引数を\ac@output@iで変換
\def\ac@output@ii#1#2#3{%
\expandafter\ac@output@iii\expandafter{\romannumeral-`0\ac@output@i{#3}}{#2}%
}
% 第一引数を\ac@output@iで変換
\def\ac@output@iii#1#2{%
\expandafter\ac@output@iv\expandafter{\romannumeral-`0\ac@output@i{#2}}{#1}%
}
\def\ac@output@iv#1#2{A(#1,#2)}
\makeatother
\begin{document}
\def\AckOutput{\noexpand\AckOutput}
% \AckOutput{A(1,1)}\AckOutput{A(0,A(1,0))}\AckOutput{A(0,A(0,1))}\AckOutput{A(0,2)}\AckOutput{3}
\message{^^J\Ack{1}{1}^^J}
% 出力のための\AckOutputの定義
\newbox\AckBox
\newif\ifAckFirst
\AckFirsttrue
\def\AckOutput#1{%
\ifvoid\AckBox
\setbox\AckBox=\hbox{$#1$}%
\copy\AckBox
\else
\ifAckFirst
\AckFirstfalse
${}=#1$%
\else
\par\noindent
\hskip\wd\AckBox
${}=#1$%
\fi
\fi
}
\noindent
\Ack{3}{1}
\end{document}
0 件のコメント:
コメントを投稿
コメントの追加にはサードパーティーCookieの許可が必要です