チョットむずかしかったです。
某ZR流パラメタトークン思考法
課題
次のTeXコードを実行したときのTeXの動作を、字句解析を中心にして述べよ。
※LaTeXフォーマットで\makeatletter
の状態のカテゴリコードを仮定する。
\toks@{\def\xx@#1}\the\toks@{!#1}
某ZR氏の解答
表記
動作の解説
\toks@
を読み [toks@] と解釈する。\toks@
(0番のtoksレジスタ)に対する代入文を開始する。{
を読み ‹{›₁ と解釈する。これ以降は代入すべきトークン列の内容であり、展開抑止の状態に入る。\def\xx@#1
を読み、[def] [xx@] ‹#›₆ ‹1›₁₂ と解釈する。}
を読み ‹}›₂ と解釈する。代入すべきトークン列が確定し、展開抑止が終わる。\tokS@
に“[def] [xx@] ‹#›₆ ‹1›₁₂”が代入される。\the
を読み、[the] と解釈する。プリミティブ\the
の引数を待つ状態になる。\toks@
を読み [toks@] と解釈する。\the
の展開を行う。\the
プリミティブの引数がtoksレジスタなので、そのレジスタの内容の“[def] [xx@] ‹#›₆ ‹1›₁₂”をバッファに吐き出し、以降はここからトークンを読み出す。- [def] を読む。
\def
文を開始し、展開抑止の状態に入る。以降に続く「代入先トークン」「パラメタテキスト」「置換テキスト」を待つ。 - [xx@] を読む。これが「代入先トークン」である。
- ‹#›₆ ‹1›₁₂ を読む。パラメタテキストの読取の途中に“‹#›₆ ‹1›₁₂”が現れたので、パラメタトークン“①”に置き換える。
- (バッファのトークンが尽きたので再びソース文字列から読む。)
{
を読み ‹{›₁ と解釈する。ここでパラメタテキストが終わり、“①”と確定する。!#1
を読み、‹!›₁₂ ‹#›₆ ‹1›₁₂ と解釈する。置換テキストの読取の途中に“‹#›₆ ‹1›₁₂”が現れたので、パラメタトークン“①”に置き換える。}
を読み ‹}›₂ と解釈する。ここで置換テキストが終わり、“‹!›₁₂ ①”と確定する。- すなわち、以下の引数を以て、
\def
文が実行される。展開抑止が終わる。`- 代入先=[xx@]
- パラメタテキスト=①
- 置換テキスト=‹!›₁₂ ①
補足
つまり、某ZRのモデルでは、パラメタトークン(① など)は「パラメタテキスト」や「置換テキスト」のトークン列の内容には現れるが、入力バッファの中には決して現れない。
もしこの後、\xx@?
を実行したとすると、[xx@] ‹?›₁₂ の展開結果として、置換テキストの ① を ‹?›₁₂ に置き換えたトークン列である“‹!›₁₂ ‹?›₁₂”がバッファに吐き出される。この場合もパラメタトークン自体はバッファには現れない。
2021年のパズル年賀状
今年の年賀状。
以前に述べたとおり、年賀状にはその年の数に関連した数学パズルを載せるのが通例である1。しかし去年はパズルを作っている時間が8時間しかなかったので2、割と普通の(ある意味で)虫食い算で済ませた。
肝心のパズル問題の部分の文字が小さくで読みづらいが、以下のように書かれている。
以下の条件に従って、割り算の虫食い算を解きなさい。
- 横に並ぶ一連の数字を全体で一つの整数と考えたとき、それが3の倍数または3のつく数である場合は赤色のマス、そうでない場合は緑色のマスに入れなければならない。
※例えば、□□には35や24は入るが、25は入らない。
一番上にある新年の挨拶はトク・ピシンではない。(何の言語でしょう?)
この問題は、TeX芸人にとってはお馴染みの「ナベアツ」(3の倍数と3のつく数字でアホになる)が元ネタになっている。条件が少しわかりにくいが、以下のような意味である。
新年の挨拶の言語はビスラマ語でした。
(※ちなみに、トク・ピシンでの新年の挨拶は“Hepi Nu Yia!”です。)
例によって、暮れのご挨拶的な何か
2020年はイロイロとアレな年でしたが、「自分にとっての2020年」を振り返ってみました。
今年は
ゆきだるま☃がいっぱい回る年でした(素敵🙂)
来年は
ゆきだるま☃がもっといっぱい回る年になってほしいですね(素敵🙂)
ZR「いやー、やっぱり、半日でネタを仕上げるのは無理でしたねー」
*「だったら無理にブログ記事を書く必要ないじゃん!」
ZR「というわけで、来年も当(くだらない)ブログをよろしくお願いします!」
今年も Merry TeXmas! ― \end{texadvent2020}
TeX & LaTeX アドベントカレンダー 2020
アドベントカレンダー完走!
^^`^^;^^`\^^$^^%^^&\!^^;\^^$^^%^^&^^=^^`\!\^^:#1^^;^^=^^`%^^'^^`1~^^`^^"
\!\[{^^2}\!\<{%% --:--:--:--:--:--:--:-- 2 @ 2 @ --:--:--:--:--:--:--:--
\^^#^^3^^.^^!^^-^^%}\!\]{^^3}\^^:?\!\>{\^^%^^.^^$^^#^^3^^.^^!^^-^^%}%**%
\!\={\^^,^^%^^4}\=\?\^^#^^!^^4^^#^^/^^$^^%\=\#\^^:\?`*\?`\^\?`.\?`\*%[#\
\?`/\?`\^\!\|{^^5}\=\@\^^%^^$**%**&\?`\@\?`//*\=\+%+5.//)=\?..{**!};@^^:
\..!^^$//6..!^^.//#..%\=\&\..'//,^^/.."^^!//,\#6\=\%\^^,**#..#///..$**%%
\@\_{\<..,**/..7^^%\[^^#..!\]..%\>}\!\^^!{\?\(\?`+\%\(\)\+\(\@^^.//%%7;)
\+\)\@**.^^%}\=\^^"\**!\=\^^#\..!\!\..${\^^!\**"\..#}\=\..%\..$%.:8:*:8:
\@\({\**$//!**9}\@\){\<//9..%**!\[\>}\!\**'{{\)`/\+\)\@...**%\(\)\+\(\)%
\//$\..%\^^!\..$\+\)\?`\^\//$\^^%\_{\&\!\..:@^^:{%%.(.**$!\\;//:%@>./++.
^^%**,//&..$**&^^/^^&^^#//'..%//&//-**&^^%..&**.//'//$//&//#..&**,**&**!
//'..#..'..#**'//+^^'^^#..'**$//&//!..&**.//&^^$^^&**!//&**,**&**/..&...
**&**%**'//-^^%^^,..'^^%..'**#..&**%^^'`//&**!..&//#**&^^+..&**!^^&^^'%^
**&^^%//'^^+**'//(**&//#//&///**&//,..&../**'//"**"..,**'^^$//&..)..&//+
..'..*.."^^,..'**#..&//#..'^^#..&...^^&/////'//'^^&..-//&..!//&^^...'//-
**%//,^^'..%**'..#..&..%**'^^$**&..)**&**+**'***//&..,**&^^)**&.."..'^^"
^^&^^!^^'**"**'**)^^'^^+**&..$//&//%//&**#..&/////'**"//&..!**'^^$^^&//)
//&..///&...^^'**#^^"**.//'^^$..&//%..'..(**'**$**'..-//%^^,..'`**&//!%/
//&//'^^&**%^^&..#//&../^^&**,//&../..'^^"^^'..+^^&//'**'^^"..&^^%..&^^%
//&//.//"..!**#**)^^#^^%^^"**!**'//"^^&..%..&//$//"**!**#//#..#`**"..!%*
//&^^"//&^^,..&//!//&^^#..&//+^^'..-..%//,^^&..#**&^^///&^^,//&/////'**"
..&//,^^&..%//'..$**'//+//&**&**&^^!**&..#..&**%..'**-**'..+//'//"^^&..%
**&..$//"..!//#**#..#`//"..!**'..)**&^^%**&**,^^&**,..&../..'^^'//"//!%.
..#//'..#`**'**-..%**,**&^^#^^&**/**&^^,..&^^/**'^^"//&//,..&..%..'//$%^
**'**+..&**)..&**...&..+//'**-..'//+**&^^&//&**!//&**#^^&**%**"..!^^#..#
..#`//"..!//&**"^^&**,^^&//!**&^^#..&**+//'..-//%//,**&...//&//%//'^^'%/
^^&^^#//&**/**&..-^^&..-^^&..!**&**.**&..$.."..*//'//+//%**,^^&^^&^^%..$
..'**-**'//+..%^^,^^&^^&..&^^/**&^^.**'//$**'//#//&^^)//'^^*..&//%^^'**+
**#^^!**#..$..'**-^^'**+**#`^^'..-**%**,//&..&..&///..&^^.//'^^$//&**&%*
**&^^!**&**-..&//)^^&**,**'..)//'..+**'`**&//(**'**&^^'//-**%//,//&^^&%/
^^&../**&**.**'//$**'^^#..&..%..'**"..&**)**&..%**'..#//'^^+//&**"..&..#
^^'^^-//%..,..'**#..&//%..&//,//&//%..&..#**'..$**&..&//&../..&**.//'//$
**'//-^^%^^,^^&**"^^&//%**&^^'**&^^)^^&^^.^^'**+//&**$//&../..&//#**'^^%
^^&//-^^&..%//&//.//'..$**'//-..%//,^^&**"^^&^^%..&//'**&..)^^&^^.^^'//+
..'//$..&//)..&**+//'*****'`//&..)^^&**#^^'**$//'..%**'^^"//&**%..'^^-%.
..%**,^^'**%**'**#//&**%//&**!**'..#..&//"^^&^^/**'**%**&//.//&..$**&..)
**&^^.^^&//'//&//"..&**/..'..(**"..(//#`.."..,^^#`//"^^)//'.."**&//%%^^)
**&^^#**'^^$**&..!//&^^.**&..'//&..,..&^^%//"^^(**#^^!..#`**"^^,^^#**!%.
**#`//"^^)//#..+^^%**,..&..&^^&..)//&..,//&^^,**%..+**&**&..&^^!//&//#%*
//&//%..%..-.."**(**#^^%.."//,^^#..%^^"**)**&//#^^&//)**'.."**&//#..&//,
^^&^^%//%..+//'.."..&^^!**&..$//&**)..'//%..'//#//#//-//#^^#.."...//#..%
..%..-**#**+**%^^,**'`..&..!//'^^$**&**(^^%^^+..&//)^^&//.^^&//+//"^^,%.
^^&../**'//%..'//$..#**-^^#..$^^#`**"**,..&**)//&//.**#**-//#**!//#^^$%^
^^#`.."^^,**&..$//&**%^^&//#**&///**'^^"^^&**!//'**$**&//%.."..,//&^^$%^
..&^^%**&^^#..&..///'**"**&..!..'**$**&**)^^&**/**&//...#..-//'**+..'..$
..&^^%..'..(**'^^$//"`..&//!**&..,^^&..///&^^...&**'**"`//'`//&**!%//(**
^^'^^$**&..(//"..,//'**$..&..%..'//(//'..$..#..-..'**+//'**,//%**,^^&**&
^^%^^$//'**,..%**#..$//%^^$..%**"`//%//)^^$..///%..%^^"`..$^^...$^^%%//-
//%^^(//%^^$.."`**%//)//$//%//$^^!..%..".."//!..'..-^^'..-**%**-^^"//(%*
^^#^^#**"//,//#^^&.."//)//'//$^^&**///"//(..#..'^^"//,..#//&//"^^)//#**+
//%^^,^^&**&^^&///**'**"//&**%^^&//!//&..#^^&..(^^%//,^^'**(**"`//&..)%/
**&//...'..+^^#^^#//"**.//#..'**"**,**#//&..".....#..#..'..-//'..+^^%//,
^^&^^.//&^^/**&..$**&^^%//%//+..'**$..&^^%//'^^(**'^^$..#..-^^&**)^^&^^.
//&//+..%^^-**&//!//'^^$**"**(..%//,^^'//(**"^^,//#..$.."^^.//#^^%^^"^^)
..'..+**%**,//'**#^^&^^#**'//#//&**.//&^^/..'^^'**&^^-**&//!**&.....%//+
//'//#..&**#..&^^!..&**,//&**%..#..-^^#//&**"//,**&.."**&**///&//$**'//)
^^"//,^^&//"..'..%..'..$^^'//$^^&..///&**.^^'**#**#..-^^&^^)//&//...&//+
//"^^,//&..-^^'//%^^&**&..&**&//&**,^^&//%**'^^"**#**-^^&**)**&^^.^^&..+
^^%//-..'^^-^^#..+**'..-..#^^+//%//,..&..%//&...**&..$**'^^+**'^^$..&**)
//&**+..'^^*//'`^^&**)..&//#..'//$..'**%^^'^^"^^&^^%..'//-**%//,//&**%%/
..&...^^&**$^^'**+..&**$//&../..&^^#//'^^%..&..-..&**%..&//.^^'..$**'//-
}}\//:*}\^^:$}\..'\(`../\+\(\?`\{\%`,=\(\+\(\?`\&\%`.=\(\+\(\?`\&\%`;=\(
\_{\@\^^!{,..$//&\|...**%\]^^#**!,..%**(^^%;}\@\^^"{\]**#**!^^..**/..+%)
//%^^.\]}\@\..%{**%;,..!^^.^^$//!**&.^^%\[}}{\<\**%\>\=\<\+\=\%%% __ __
\+\&\@\^^:@^^!{\&\=\%\<}}\**:@..!\<\^^%\>\=\%\.."\>\+\&\@%.^../%% / |_>
\..:..:..$../{\<\.."\>{\<\..!\>{\..:@..:}}\#?}\#;}\^^:^^:^^$^^/%% /_ | \
pdflatex
でコンパイル)というわけで、9回目の開催となる TeX & LaTeX Advent Calendar 2020 も、素敵なTeXネタを途絶えさせることなく例によって無事にクリスマスの日を迎えられました。今年の参加者は全部で19名、うち5名が初参加でした。参加者の皆様に心からの感謝を例によって捧げたいと思います。
今年の重点テーマは「このパッケージもスゴイ!」でした。改めて見返すと、テーマに沿った記事が割と多く、たくさんのパッケージが紹介されました。その一覧をあげておきます。
- bxpdfver
- fewerfloatpages
- hmtrump
- jlreq-deluxe
- manfnt
- mathabx
- mleftright
- pdfx
- pxchfon
- pxjodel
- qrcode
- scsnowman
- scsnowman+
- tcsushihead
- tegaki
- tikzducks
TeX & LaTeX Advent Calendar 2020 を楽しんでくれた皆さんに、
例によってありがとう!
そして
*「やっぱり……」
ZR「例によって……」
*「………………」
徹底検証! SATySFiはLaTeXの代わりになるか
(21日目は bd_gfngfn さん、23日目は amutake さん です。)
時の経つのは早いもので、SATySFiもその正式公開1からもう2年以上も経っています。この間にSATySFiを取り巻くエコシステム(例えばパッケージマネージャであるSatyrographosなど)も徐々にではありますが整えられつつある状況です。
今の状況に至ったところで、改めて「SATySFiの元来の目的」について思い返してみましょう。
初心忘るべからず。#satysfi #TeX以外 pic.twitter.com/RPvhgwPYzv
— 某ZR(ざんねん🙃) (@zr_tex8r) 2020年11月8日
そう、SATySFiは「LaTeXを置き換える」ことを目的にして開発されたのでした。
現状のSATySFiは「LaTeXを置き換える存在」になっているのでしょうか?
そもそもLaTeXとは何だったのか
この疑問に答えるには、まず「LaTeXの存在意義」という究極の問いについて考察する必要があります。LaTeX(拡張エンジンを含む)を使うと数式や日本語や英語などを高い組版品質をもって出力できるわけですが、LaTeXは究極的に何のためにあるのでしょうか?
LaTeXなんて、ゆきだるま☃さえ出力できればいいんですよ#TeX #えっ
— 某ZR(ざんねん🙃) (@zr_tex8r) 2016年3月9日
極めて本質的な答えですね。しかし最近は状況が少し変わっています。
新しいLaTeXがイロイロと🍣なので、☃を回すしかない……😫 #ナントカ pic.twitter.com/IMcB8QaUYd
— 某ZR(ざんねん🙃) (@zr_tex8r) 2020年9月13日
正立☃️→倒立☃️→回転☃️
— ワトソン (@wtsnjp) 2020年5月10日
という展開があったようだ.#ナントカ #ナントカ考古学
このように、最近では単に☃を出力するだけでは不十分で、☃を回転させることまで要求されるようになっています。
まとめると、LaTeXの目的は究極的には「☃を回すこと」であると結論づけて間違いないでしょう2。
SATySFiで☃を回せそうにない話
LaTeXの目的についての本質的な理解が得られたところで、いよいよSATySFiについて考えてみます。果たしてSATySFiで「☃を回すこと」は可能でしょうか。
LaTeXでは「PDF上でのアニメーション」を実現するためのanimateというパッケージがあり、また、この機能を利用して「テキストを回転させる」ためのtcfaspinというパッケージが作られています。tcfaspinを使うことで簡単に☃を回転できます。
- これからの時代はLaTeXを回転させよう!(Qiita/@zr_tex8r)
一方で、SATySFiには現状で「PDF上でのアニメーション」を実現するための機能が欠けています。このためSATySFi上でfcfaspinに相当するパッケージを実装することは、残念ながら現状では不可能です。やはり、SATySFiはLaTeXを置き換える存在にはなりえないのでしょうか。
それでもSATySFiで☃を回したい話
一見して絶望的な状況ですが、ここでまた最近のLaTeXのトレンドを見てみると、希望が見えてきます。
美文書第☃︎版「現代的なTeX(LuaTeXなど)の出力形式はPDFというもので、Adobe Readerなどのソフトウェアで開いて中身をみられますが、ツイッタァーに上げるためにはPNGやGIFなどの画像形式に変換する必要があります」#TeX #ナントカ #えっ
— 某ZR(ざんねん🙃) (@zr_tex8r) 2020年8月18日
すなわち、LaTeXの主戦場が最近ではPDF文書からGIFアニメ画像に移行しつつあるといっても過言ではないでしょう3。
とはいっても、LaTeXエンジンにGIFアニメ画像を出力する機能はありません。「LaTeXでGIF画像をつくる」にはtcspingifという補助のスクリプトを利用する必要があります。
- LaTeXでつくる「回転ゆきだるまGIF画像」(マクロツイーター)
このtcspingifは「普通の(静止した)複数のPDF画像からGIFアニメ画像に変換する」という処理を行うものです。そのことを考えると、SATySFiについても可能性が見えてきます。tcspingifをSATySFiに対応させればよさそうです。
tcspingifで「TeX以外」する話
というわけで、tcspingifを少し改修してみました。
- tcspingif.pl(Gist/zr-tex8r)
今まで、エンジン(-e
オプション)にはLaTeXのコマンドを指定することのみを想定していましたが、ここで任意のコマンドを指定することにします。この場合、オプションの指定の方式がLaTeX用のものだと不便なので、もう少し汎用的な呼出形式を--interface=env
で選択できるようにしました。
従来(既定値の--interface=latex
を指定した時)は「nフレーム中のmフレーム目」のPDF画像を生成するのに特定の呼出形式でLaTeXのコマンドを起動していた(参考)のですが、--interface=env
を指定すると代わりに以下のコマンド行が実行されるようになります。
<コマンド名> <入力ファイル> <一時出力ファイル>
<コマンド名>
は-e
オプションの値。<入力ファイル>
はtcspingifに与えた入力ファイル名。<一時出力ファイル>
はこのコマンドが出力すべきPDF画像のファイル名。(実際のtcspingifの実行中には自動生成のファイル名が指定される。)- 以下の環境変数の値が設定される:
FASTOPTICKS
:上の説明中の「m」の値。FAALLTICKS
:上の説明中の「n」の値。
すなわち、上記の規約を満たすプログラムを作成して、そのコマンド名を-e
に指定すればいいわけです。
tcspingifでSATySFiする話
tcspingifの新機能に基づいて、「SATySFIでフレーム画像を生成する」仕組をつくってみます。次のような仕様にします。
- 入力ファイルは「SATySFiのパッケージファイル(
*.satyh
)」とする。 - パッケージの中で以下の函数
frame
を定義する必要がある。frame
: int → int → documentframe
m n は出力したいGIFアニメ画像の「n フレーム中の m フレーム目」の画像を表す文書。
この仕様とtcspingifの仕様を繋ぐためのシェルスクリプトを用意します。
#!/bin/bash set -eu if [[ ! ( $# == 2 && -f $1 ) ]]; then exit 1 fi stop=$((${FASTOPTICKS})) all=$((${FAALLTICKS})) ibase=${1%.satyh} obase=${2%.pdf} if [[ ! ( 0 -le $stop && $stop -lt $all &&\ $1 != $ibase && $2 != $obase ) ]]; then exit 1 fi cat <<EOT >$obase.saty @import: $ibase frame $stop $all EOT ${SATYSFI:-satysfi} $obase.saty if [[ ! -f $2 ]]; then exit 1 fi rm $obase.saty
- tcsg-satysfi(Gist/zr-tex8r)
※ファイル名をtcsg-satysfi.sh
→tcsg-satysfi
に変更すべき。
SATySFiでとにかく☃を回す話
これで準備は整ったので、いよいよ☃を回すことにしましょう。必要なのは、「☃を回す」ためのパッケージファイルを作ることです。そこで、次のようなframe
函数を定義するscframeパッケージを実装します。
- 「
frame
m n」は時計回りに (m/n) 回転分だけ傾いた☃の絵を中央に配置した文書を作る。
この仕様は現在のSATySFiで十分に実装可能です。
- scframe.satyh(Gist/zr-tex8r)
新版のtcspingifとtcsg-satysfiを実行可能にした上で、scframe.satyh
を置いたディレクトリで以下のコマンドを実行しましょう!
tcspingif -e tcsg-satysfi --interface=env -t 32 -b 2 scframe.satyh
次のように端末に表示された上で、結果のGIFアニメ画像ファイルscframe.gif
が出力されました。
$ tcspingif -e tcsg-satysfi --interface=env -t 32 -b 2 scframe.satyh tcspingif: convert: scframe.satyh -> scframe.gif tcspingif: parameters: density=72; ticks=32 tcspingif: generate frame: 0 tcspingif: generate frame: 1 tcspingif: generate frame: 2 ……(略)…… tcspingif: generate frame: 31 tcspingif: generate gif: scframe.gif
scframe.gifを表示させてみると……。
SATySFiで☃を回せました!
まとめ
やっぱり「回転する☃」はトッテモ素敵😊
例によってアドベントがはじまった ― \begin{texadvent2020}
TeX & LaTeX アドベントカレンダー 2020
TeX界における年末の恒例イベントとなったような気もするアドベントカレンダーが今年も始まりました。
重点テーマ
今年の重点テーマはコレです。
このパッケージもスゴイ!
スゴイLaTeXパッケージ、いっぱいありますね。その中には、スゴイけどあまり知られていないものもあるでしょう。「このパッケージはもっと普及すべきだ」と思うパッケージを紹介してしまいましょう!
※例によって、重点テーマは「必須」ではありません。あらゆるTeXネタを歓迎しております。
で、初日のネタは
こちらになります。
- LuaLaTeX で pdfx パッケージを使い PDF/A に準拠した PDF を作る(Qiita/@trueroad)
まだ参加できます!
TeX & LaTeX Advent Calendar 2020(#texadvent2020)は
まだまだ参加者募集中です。
LaTeXなネタ、TeX言語なネタ、アレなネタ、アレレなネタ、アレアレなネタ、ア~レ~なネタ、アレッアレなネタ、超絶アレなネタ、その他ナントカカントカ、お持ちの方はぜひぜひ、
ご参加おねがいします!
いつの間にかもう空きが残りわずかとなっています。登録はお早めに!