マクロツイーター

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

メモ:新しいLaTeXの文書プロパティ機能の現状

コレに関する話。

blog.wtsnjp.com

文書プロパティについて語りたい

この記事の説明(オリジナルのLaTeX Newsの内容もほぼ同じ)を読むと、文書プロパティ1は従来の相互参照(\label\ref)の機構を拡張するもので、しかも値を“展開可能”な方法で取得可能であると説明されている。これを見る限り、この新機能によって、長年TeX言語プログラマを悩ませてきた「ラベルに紐づく値を取得する確実な方法がない」という問題が解決されるように思える。

しかし実際にチョット調べてみたところ、少なくとも現状ではこの希望的観測は的外れで、実際には文書プロパティの機能は「ラベルに紐づく値の取得」には使えなかった(ざんねん🙃)、という話。

「ラベルに紐づく値の取得」が難しい

以下にLaTeXのフツーの相互参照を利用した文書を示す2

\documentclass[a4paper]{article}
\begin{document}
\setcounter{section}{41}% 節番号を42から始める
\section{Duck}\label{sec:duck}% ラベルを付けた
Quack!
\section{Conclusion}
Section~\ref{sec:duck} (p.~\pageref{sec:duck}) is dull.
\end{document}

出力

この文書ソース中で、\ref{sec:duck}\label{sec:duck}を付与した節の番号、\pageref{sec:duck}は当該の節のあるページの番号を出力するために使われている。ここで注意すべきなのは、LaTeXの仕様としては\ref\pagerefはあくまで番号を「出力」する命令であり、番号を「取得」するための手段は用意されていない、ということである。

相互参照の使い方によっては単に番号を出力する以外の使い方をしたい場合もある。ここでは(極めて人工的な例であるが)次のような命令を実装することを考えてみよう。

  • \myRefSum{‹ラベル›}‹ラベル›に紐づくカウンタ3の番号(\ref{‹ラベル›}の値)とページ番号(\pageref{‹ラベル›}の値)の合計の値を出力する。

この命令を実装しようとすると、\ref\pagerefを単純に使って番号を出力するだけでは間に合わず、\ref\pagerefの値を(トークン列なり内部整数値なりの形で)取得する必要がある。しかしLaTeXの仕様ではそもそも値を取得するための手段が用意されていないので、結局、仕様に従う限りは実装は不可能になってしまう。

どうしても\myRefSumを実装したいのであれば「LaTeXカーネル内部実装に依存するコードを書く」という強硬策に頼る4ことになるが、その場合でも現実問題として相互参照周りの内部実装は様々な要因5で変動しやすいため、「特定の仕様通りに確実に動作する」ようなコードを実装する(そして維持する)のは極めて困難なのである。

新機能で「ラベルに紐づく値の取得」ができたらよいのに

新しい文書プロパティ機能では\RefPropertyという展開可能な命令でプロパティの値を取得できる。

  • \RefProperty{‹ラベル›}{‹プロパティ›}:[展開可能]‹ラベル›に紐づくプロパティ‹プロパティ›の値。

プロパティを定義及び記録する命令6は従来の\label\refとは別に存在するのであるが、一方で仕様書(ltproperties-doc.pdf)には次のようなことが書かれている。

  • カーネルで予めlabelpageというプロパティが定義されている。
  • labelは従来の相互参照における\refの値(ラベルに紐づくカウンタ番号出力)に相当する。
  • pageは従来の相互参照における\pagerefの値(ラベルに紐づくページ番号出力)に相当する。
  • 従来の\labelの「ラベル」はそのまま文書プロパティ機能における「ラベル」にもなる。

これを読む限りは、いかにも次のような仕組みになっていそうである。

  • \RefProperty{‹ラベル›}{label}により\ref{‹ラベル›}の値が取得できる。
  • \RefProperty{‹ラベル›}{page}により\pageref{‹ラベル›}の値が取得できる。

本当にそうなっているのか確かめてみよう。

\documentclass[a4paper]{article}
\begin{document}
\setcounter{section}{41}
\section{Duck}\label{sec:duck}
Quack!
\section{Conclusion}
label=\RefProperty{sec:duck}{label};
page=\RefProperty{sec:duck}{page}.
\end{document}

出力

ありゃ、うまくいかない(ざんねん🙃)

値の部分にはプロパティの「既定値」が出力されている。どうやら、ラベルに紐づくプロパティの値が記録されていないようである。つまり、少なくとも現状の仕様においては、従来の\label命令では新機能のプロパティの値は記録されないようにみえる。

余談:値が取得できないなら警告すべきでは

先の文書のビルドの際には警告は出ないのであるが、取得するプロパティ値が記録されていないなら(従来の相互参照において指定した\labelが見つからない時と同様に)警告が出てほしい気がする。実は警告を出す命令は別にある。

  • \RefUndefinedWarn{‹ラベル›}{‹プロパティ›}‹ラベル›に紐づくプロパティ‹プロパティ›の値が記録されていなければ警告を出す。

なぜ取得する命令\RefPropertyで警告を出さないのかというと、そうすると展開可能でなくなってしまうからである。\RefPropertyを使うプログラマが“適切なタイミング”で適宜\RefUndefinedWarnを実行する必要がある。

先の文書ソースの7行目に次のコードを追記する。

\RefUndefinedWarn{sec:duck}{label}
\RefUndefinedWarn{sec:duck}{page}

すると文書のビルド時に警告が出るようになる。

LaTeX Warning: Property `label' undefined for reference `sec:duck' on page 1 on
 input line 7.

LaTeX Warning: Property `page' undefined for reference `sec:duck' on page 1 on
input line 8.

やはりプロパティ値は記録されていないことが判明した。

どうにかして「ラベルに紐づく値の取得」してみる

当然であるが、「\labelを実行する際に新機能のプロパティの値を同時に記録するようにする」と\RefPropertyで値が取得できるようになる。例えば、以下のような命令\myLabelを定義してこれを\labelの代わりに使うという方法が考えられる。

※先述の通り相互参照と文書プロパティの「ラベル」は共通(名前空間を共有する)なので、同じ名前のラベルを両方に使うことはできない(ラベル重複になってしまう)。そのため、プロパティの方のラベルの名前には接頭辞(my/)を付けている。

%% \myLabel{<ラベル>}: 相互参照と文書プロパティの両方のラベルを置く.
% ※プロパティの方のラベルには"my/"の接頭辞を付ける.
\NewDocumentCommand\myLabel{m}{%
  \label{#1}% 相互参照のラベル配置
  \RecordProperties{my/#1}{label,page}% プロパティ記録
}

これにより\RefPropertyで値が実際に取得できるようになるので、今度は先述の\myRefSumの実装が可能になる。実際に\RefPropertyでの値の取得と\myRefSumの実行をする完全なコードを以下に示した。

\intevalは整数式の計算をする命令。

\documentclass[a4paper]{article}
%% \myLabel{<ラベル>}: 相互参照と文書プロパティの両方のラベルを置く.
% ※プロパティの方のラベルには"my/"の接頭辞を付ける.
\NewDocumentCommand\myLabel{m}{%
  \label{#1}% 相互参照のラベル配置
  \RecordProperties{my/#1}{label,page}% プロパティ記録
}
%% \myRefSum{<ラベル>}: 例のアレ.
\NewDocumentCommand\myRefSum{m}{%
  % 値が記録されてなければ警告
  \RefUndefinedWarn{my/#1}{label}%
  \RefUndefinedWarn{my/#1}{page}%
  % 両方の値が記録されていれば合計値を出力する
  \IfPropertyRecordedTF{my/#1}{label}{%
    \IfPropertyRecordedTF{my/#1}{page}{%
      % 展開可能なので \inteval 中で使用可能
      \inteval{\RefProperty{my/#1}{label}%
               +\RefProperty{my/#1}{page}}%
    }{}%else
  }{}%else
}
\begin{document}
\setcounter{section}{41}
\section{Duck}\myLabel{sec:duck}
Quack!
\section{Conclusion}
% 番号を取得する例
label=\RefProperty{my/sec:duck}{label};
page=\RefProperty{my/sec:duck}{page}.\par
% \myRefSum の例
sum=\myRefSum{sec:duck}.
\end{document}

出力

なぜこんな仕様なのか

仕様書(ltproperties-doc.pdf)に次のような記述がある。

Currently the code has nearly no impact on the main \label and \ref commands as too many external packages rely on the concrete implementation. There is one exception: the label names share the same namespace. That means that if both \label{ABC} and \RecordProperties{ABC}{page} are used there is a warning Label ‘ABC’ multiply defined.

(訳)
現状では、大本の\labelおよび\ref命令はこの[新機能実装の]コードの影響をほぼ受けない。外部パッケージでその具体的な実装に依存したものがあまりに多いからである。一つだけ例外がある:ラベルの名前は名前空間を共有している。つまり、\label{ABC}\RecordProperties{ABC}{page}の両方を使うと、Label ‘ABC’ multiply definedの警告が発生する。

従来の相互参照と文書プロパティを「全く無関係な別個のもの」と位置付けるならば、両者でラベルの名前空間を共有するのは明らかに不合理なはずである。共有させているということは、恐らくLaTeXチームは「究極的には両者を統一したい」と考えているようにも思える。今は既存のパッケージの問題があって実現できていないようであるが、将来的には何か手を打つのがもしれない。

まとめ

とりあえず今のところは、ざんねん🙃(ざんねん🙃)


  1. 「文書プロパティ」はLaTeXの概念であり、PDFの「文書のプロパティ」とは無関係である。
  2. 相互参照を利用しているので、文書のビルドの際には2回タイプセットが必要である。以降の例の文書でも同様。
  3. 話を簡単にするため、当該のカウンタ(とページカウンタ)の表示書式は算用数字であることを仮定する。つまり、カウンタ番号のトークン列はカウンタ値の整数として通用する。
  4. 主に「\ref\pagerefの『実装において展開可能として動作する実行パス』を利用する」という方法と「相互参照情報を保存するマクロr@‹ラベル›を直接操作する」という2つの方法がある。
  5. hyperref等のパッケージの読込によって実装が置き換わる。また、最近のLaTeXの改修でも相互参照周りの実装が変動していて、従来の実装が動作しないという不具合が発生している。
  6. 定義する命令が\NewPropertyで、記録する命令が\RecoedProperties

100万回ハローワールドするTeX言語的な方法

とあるプログラミング言語1が超絶アレなためムシャクシャしたので、チョットTeX芸してみた。

[↓お題]

[↓結果🙃]

※上記のプログラムはplain TeX用のものである。texコマンドでコンパイルするとDVIファイル、pdftexコンパイルするとPDFファイルが得られる。以下、この記事で扱うコードはplain TeXを前提とする。

とりあえず

なるべくTeX言語特有の変態な方法を使う

という方向性にこだわってみた。

参考:フツー(?)の方法

なお、元ネタで使っている方針はもちろんTeX言語でも使える。TeX言語チョットデキル人であれば思いつくであろう2

\let\z.\def~{\z}\def\y{\edef~{~~~~~~~~~~}}\y\y\y\y\y\y
\def\z{Hello world!\par}~\bye

TeXを対話モードで使っていて「反復処理を書きたい」という場合に、このパターンを実際に使うことがたまにある。(TeXでループを書くのは面倒なので。)

変態な方法

{\catcode`\m=\active\gdefm{\hbox{Hello world!}$\par$}}
\mathcode`m="8000$\romannumeral1000000000\relax$\bye

ポイントは\romannumeral1000000000である。ローマ数字で“10億”を出力しようとしているが、(TeXの)ローマ数字で最大の数字は“m=1000”なので、結果的にTeXはこのコードをmを100万個並べた文字列に展開する。これでループ的な実行制御なしで“同じトークン100万個”を得ることに成功した🙃

ただしこのmのカテゴリコードは123なので、このままでは“m”以外のテキストを出力するのには使えない。カテゴリコードが12の文字トーク4でマクロを実行させたい……となると、math-acriveを使うことが考えられる。

文字のmath codeを"8000に設定することを“math-active”という5。文字をmath-activeにすると、数式モード中で当該の文字の(カテゴリコードが11または12の)文字トークンが実行されたときに「代わりにその文字のアクティブ(カテゴリコード13)な文字トークンが実行される」という動作になる。標準のplain TeX(やLaTeX)では数式モード中での'の入力でプライム記号(\prime)が上添字として出力されるが、この挙動は'をmath-activeにすることで実現している。

今の場合はmの入力で“Hello world!”を出力させたいので、アクティブなmにマクロを定義した上で\mathcode`\m="8000を設定する。その上で、数式モードに入って\romannumeral1000000000を実行すればよいことになる。ただし数式モードに入るのは飽くまでmath-activeのためで文字列自体は非数式で出力したいなので\hboxを使う。さらに「数式モード中では改段落ができない」のを回避するために「一旦数式モードを終結してから改段落してまた数式モードに入る」という対策をとった。

まとめ

新しいテフライブが無事にリリースできるといいですね😊(まとめろ)


  1. ただしTeX言語以外😲
  2. 先頭の\let\z.は「\zを(一時的に)展開不能にする」ために入れている。1行目を実行した時点で~の意味は「\zを100万個並べたもの」に展開されるマクロになる。
  3. \romannumeralの展開結果は\the文字列なのでこのmのカテゴリコードは11ではなく12である。
  4. e-TeXを前提するなら\scantokensを使うという手段もありそうだが、これは実際にやってみると\scantokensのバッファが100万文字に耐えられずに失敗した。
  5. もしかしたら“math-active”はオレ用語なのかもしれない🙃

Typstで“calc.abs(-8)”はメソッド呼出なのか

Typstのメソッド呼出を完全に理解する話

Typstの一部の型はメソッドをもつ。例えばarray型の値は自身の長さ(要素数)を取得するためのlen()メソッドをもっている。

#let ary = (1, 2, 3)
#ary.len() //==> 3

ここで注意すべきなのは、これはary,lenという(function型の)フィールドに関数呼出の括弧を付けたものではない、ということである。実際、array型の値aryにはary.lenというフィールドは存在しない1

#ary.len //--> error: cannot access fields on type array

Typstにおいてフィールドの参照とメソッドの参照が異なる概念であることはdictionary型をみればさらに明らかになる。以下の例をみてわかるように、フィールドとメソッドの空間は全く別になっている。

#let dict = (foo: calc.abs, len: 42)
#dict.len     //==>42
#dict.len()   //==>2
#dict.foo     //==>abs (function値の表示)
#dict.foo(-8) //-->error: type dictionary has no method `foo`
#dict.keys    //-->error: dictionary does not contain key "keys"
#dict.keys()  //==>("foo", "len")

ということは、Typstではval.name(...)という形2の式は「メソッド呼出」を表すものであり、これとval.nameの形の「フィールド参照」とは全く別のものである、といえそうである。もし「フィールドのfunction値を呼び出す式」を書きたいのなら、val.name(...)という“形式”を回避する必要があり、簡単な方法としてはval.nameの部分に括弧を付ければよい。

#(dict.foo)(-8) //==>8 ('calc.abs(-8)'の値)

“メソッド呼出の意味論”についてはTypstの公式のドキュメントに説明がある。

すなわち、val.name(...)というメソッド呼出はtype(val).name(val, ...)と等価になる。先の例でdict.len()type(dict)dictionary3であるので次の式と等価になり、これは実際に2を返す。

dictionary.len(dict)

Typstのメソッド呼出がなにもわからない話

ところで先のdictionaryの例でcalc.absという関数を使った。これは組込のcalcモジュール(module型の値4)に属している関数で、数値の絶対値を返すものである。通常はcalc.absに関数呼出の括弧を付けて使う。

#calc.abs(-8) //==>8

何の変哲もないコードであったはずだが、ここで先の考察を踏まえるとある疑問が湧いてくる。このcalc.abs(-8)というのは「メソッド呼出」なのであろうか?

この式はまさにval.name(...)という形なので形式の上ではメソッド呼出のはずである。ただし先のdictionaryやarrayの話と決定的に異なる点がある。calc.absは実際にcalcのフィールドとして存在するのである。これはcalc.absの部分に括弧を付けても呼び出せることからわかる。

#(calc.abs)(-8) //==>8

これを踏まえるとcalc.abs(-8)は「calc.absというフィールド値に関数呼出の括弧を付けた式」でありメソッド呼出でない気がしてくる🤔

やっぱりメソッド呼出でありそうな話

こういう関数を考える。

#let call-len(val) = val.len()

Typstは動的型の言語であるため、valの型は実行時にしか決まらない。もしここで、valにarrayの値とmoduleの値のどちらも受け付けるのであれば、val.len()という1つの式が成立する以上「calc.abs(-8)ary.len()とは異なる構文である」ということはありえないことになる。実際に確かめてみよう。

[mod.typ](len()という関数をもつモジュール)
#let len() = 42
[main.typ](このファイルを実行する)
#import "mod.typ"
#let call-len(val) = val.len()
#let ary = (1, 2, 3)
#let dict = (foo: calc.abs, len: 42)
#call-len(ary)   //==>3
#call-len(dict)  //==>2
#call-len(mod)   //==>42

“期待通り”の結果になった。ということは、やっぱりcalc.abs(-8)はメソッド呼出である……?🤔🤔

やっぱりメソッド呼出でなさそうな話

calc.abs(-8)がメソッド呼出であるなら、先ほど紹介した“メソッド呼出の意味論”を満たすはずである。つまり、type(calc)moduleであるからcalc.abs(-8)は以下と同値になる。

module.abs(calc, -8)

つまり、module(type値)にはmodule.absというフィールド5がありその値は「引数のモジュールのabsフィールドの関数を呼び出す」という役割をもった関数、ということになる。もちろんモジュール内の関数名には任意の識別子が使えるので、この理屈に従うと「moduleにはありとあらゆる名前のフィールドが定義されている」というオソロシイことになる。まあ論理的にありえない話ではないので、実際に確かめてみよう。

#module.abs           //-->error: type self does not contain field `abs`
#module.abs(calc, -8) //-->error: type self does not contain field `abs`

どうやらそんなオソロシイ話はなかったようである😊 でもこれだとやっぱりcalc.abs(-8)はメソッド呼出ではない……?🤔🤔🤔

Typstのメソッド呼出がチョットデキル話

なにもわからなくなったので、処理系の実装をみてみよう。

詳細の説明は(メンドクサイので🙃)省くが、やはり、val.name(...)の形式の式の実行においてはvalの型によって解釈を変えているようである。

  • valの型がsymbol、function、type、moduleの何れかである場合6は、フィールドval.nameの値に対する関数呼出と解釈する。
  • それ以外の場合は先述の“メソッド呼出の意味論”に従う。

つまり結論としては:

  • val.name(...)val.nameとは全く別の構文である。
  • しかしvalの型によって「メソッド呼出」になったり結局「val.nameの関数呼出」になったりする。
  • calc.abs(-8)は後者に該当するので「メソッド呼出」ではない

まとめ

皆さん、そんな細かいことは一切気にせずに、どんどんTypstしましょう😃


  1. そもそも、array型の値はフィールドを一切持っていない。
  2. nameは単一の識別子に限るが、valの部分は任意の式でよい。
  3. つまり、トップレベルでdictionaryとして定義されているtype型の値。
  4. 意外かもしれないがTypstではモジュールは第一級値(first-class value)である。
  5. valがtype値である場合のval.name()は(module値であるときと同様に)フィールドのval.nameの関数呼出と同じ動作になる。例えばdictionary.lenというフィールドは実際に存在する。
  6. 本当はこの場合でもtype(val).nameのフィールドが存在する場合は“メソッド呼出の意味論”が優先されるようである。ただ型の性質を考える限り、この4つの型にメソッドが設定される可能性はほぼなさそうである。

2024年のパズル年賀状

今年の年賀状。

以前に述べたとおり、年賀状にはその年の数に関連した数学パズルを載せるのが通例である1。去年も割と余裕をもってパズル問題を作ることができて、結局(例によって)チョット変わった虫食い算になった。

肝心のパズル問題の部分の文字が(例によって)小さくで読みづらいが、以下のように書かれている。

次の条件に従って掛け算の虫食い算を解きなさい。

  •  の 6 マスおよび  の 6 マスはそれぞれ 6 面ダイス(立方体のサイコロ)の展開図になっている。

※ 6 面ダイスの向かい合う面に書かれた数の和は 7 でなければならない。

(問題の図)

一番上にあるのはベトナム語(ただしチュノム😲)での新年の挨拶。


  1. Typst用の文書テンプレートの開発に時間を取られている人が多いためか、年を追うごとに年賀状で数学パズルを見ることが少なくなっているようで感じる。

“TypstでTeXのロゴ”とか暮れのごあいさつとか

Typstで“TeXロゴ”したい話

以前に“TeXのロゴ”について記事を書きました。

この記事では、“TeXロゴ”の熱狂的なファンがTeX以外で正しい“TeXロゴ”を出力する方法について解説しています。具体的にはHTML+CSSおよびMicrosoft Wordを扱っています。

Wordで“TeXロゴ”する様子

今世間で話題になっている“TeX以外”といえば、やっぱりTypstですね。熱狂的なファンであれば、当然Typstでも“TeXロゴ”(および“LaTeXロゴ”)を使いたいところです。

※ちなみに、SATySFiにおいては標準ライブラリのpervasiveにおいて“TeXロゴ”と“LaTeXロゴ”を出力するための\TeX命令と\LaTeX命令が提供されています。

Typstで“TeXロゴ”してみた話

というわけで、つくってみました。

[texloog0.typ]
#let TeX = {
  [T]; "\u{2060}"
  box({h(-0.1667em); box(move(dy: 0.2153em)[E]); h(-0.125em)})
  "\u{2060}"; [X]
}
#let LaTeX = {
  [L]; "\u{2060}"
  box(style(styles => {
    let size = measure([T], styles)
    h(-0.36em)
    box(height: size.height, {text(size: 0.7em)[A]})
    h(-0.15em)
  }))
  "\u{2060}"; TeX
}

このtexloog0.typをライブラリとしてインポートします1

#import "texloog0.typ": *

これにより次の2つの値2が使えるようになります。

  • TeXTeXのロゴ(content値)。
  • LaTeXLaTeXのロゴ(content値)。

もちろんTypstのマークアップモードの中で使う場合には#TeX;のように書くことになります。以下で簡単なサンプルを示します。

#import "texlogo0.typ": *
#set text(font: "Harano Aji Mincho")

#TeX;言語は超絶アレ、#LaTeX;は微アレ。

☃は非アレ。

この文書をコンパイルすると以下の出力が得られます。

出力結果

無事にTypstで”TeXロゴ”できました(幸せ😊)

まとめ

今年も一年、ありがとうございました!

除夜のカントカ

ZR「というわけで、来年も当(くだらない)ブログをよろしくお願いします!」
*「アレレ、ネタ画像が完全に2年前の使い回しだ😲」
ZR「………………3


  1. 最後の*は「全てインポート」を表すもので、例えばこれをTeXに変えると#TeXだけがインポートされます。
  2. TeXLaTeXはcontentを返す関数ではなくcontent値そのものです。
  3. チョット変えたとしても誰の得にもならないので、ここは省力化することにしましょう🙃

今年も Merry TeXmas! ― \end{texadvent2023}


TeX & LaTeX Advent Calendar 2023
*  *  *

アドベントカレンダー完走!

+88508828882888288828882888288828882888288828882888288828882888288828888
-45886688758823188232882338850972086667109289025595801777770600081308710
+88231885059520663304283186539094925899696529553504406871601717178172108
-88794488238823188488238823288238823388298850699868069817901780806811808
+88758869882332887588698823228875588232885083874655954871007111180142100
+88231882338850387852755197760761265569092407858099695817891780861306510
+88232882388233882988748874882328823188232885068590854680807171110898698
+88758823882328874887558823882318850056682580903680690808986606868689868
+88233887488748823188758823882318835882388232883588507197966717689911139
+88231882388231885806866655961407706803991452429998718710718008776170680
+88238823288748875887488748875885036216936908506049916910516911110677760
-45886688507995449009980283852235697816134029592056910910618138118080118
-88645588583869533786667323616862863994861397828068610516618677418171140
-88655588758858978756751528203674989265465548532805809680868090980809685
-44488358850382095266226251250299725958992734568027740216472098128641996
-88638838865886188645887588678825886388298861887588388638804588505479064
-58865488638838865886188645883588509010769993536660568067636258528757193
-54488358850836205039656569602312185876868553300059581823731728766932539
-54588668835885877850956868012641295168766932621968345207838095713951595
-88694488358858286658562233606437326320801660830862732993102980174802493
-48869886458865458866883588584899912828169194686958332823753520924556787
-88644488358850102976993458593552658313380965991657669555580932839156143
-88645886188658869886588678835885808933583809933352526813625387374994362
-88674488231882328823388298835882328835887588231883588505692809580493989
-88638838865886188645882314488585031324335329357483651358323290968691483
-58865488638838865886188645883588298875889885856012465773851629847463189
-88734488488678824887588648834882988754882226883418850555256096407586804
+88748875412388349887543388754928875588210882688214886188988748821032885
+98821488698868869458821078875488210887543388754338875432887548822368834
+68875504619629882604588666188266288748874040887244618821626388299629885
+88758890887244884882887588648861882904076179088638838865886188645618858
-58865488638838865886188645904096162908861488688618865886350062078875885
+88288638863880457882232000788758828808877588288638861887357882604619885
+29887490039090290901209012090610903300970908879447090887144309088774406
-40890458866988758869088692040887561790788758877488666179040887190440887
+88299040446188299088758875882886988758873588288618875887278826618874904
-88766149088218873588758838826610088288670061000882088678874909088764406
-90880443007104058877614628829907887588988628808875880788758838806108862
+88266207887588988388737887490404887161490448821881006100886388648829904
-88219088758863880887588288088288388758838861886388758871702009090886702
-40488766149088218873588758838826610088288678874909088793090887770408862
+98862887588779090887210408826619078875889883887588188698875887776108869
-58870248867882990908873104048872614628829907887588288088775887276108862
-58870240788758898838875881886988758877731088620886662882990400988219885
+88754060100120405990788668808865887588737088668834886688618868864887205
+78890088618875887310288250230603012088628875410210013080488788260504882
+32024575151515251515151525151515150150153515153501882904887231024565605
+56503501525015250188290488723020245751515351515153515151565151535015205
+50188290488722902458826102887452565882610288745015015250188290488722885
-30245651515153515151515251515151565065151501515188290488203024502515305
+52515151515250152515151515015015251515151535151535151518829048872702885
+45025252525650150156501525650152501882904887260245025252525151515015105
+51506575151535151515350152515151882904882060245025252525650150150350105
+56515151515253882904882010245025351525151515152501501503501515151515205
+50152501882988740459088269887541021001020804889882604420253158864519533
+10150115135012588635125013510250135750101588635015016565016503501658863
+95016511501015125010158863510150165101501751065019588635165506155165019
+16501035886351650165101501015106501358863516501252153065225533588635523
+31521530252025315886358862882988740804883882604418825395887751508898858
+88290886025025024048876151514044060257258864560157257656065765060658863
+76506157250175675010158863579501150301532503015206588635030151357065503
+60350358863532503588758872520650351651351652065886351653252150115335885
-10158863520350175201506152015060658863520156065365725060257258863558862
+88290886025025024044012506658864501250603501150695010250695886353950695
+30350603530350665886353035060153950635010250635886350115063501250601555
-12506658863588668829044625066588645625060356150695602506955886350695069
-60350603506035066588635060350601506950635602506358863561506356250601562
-66588635886688290440102501035886450106501065060650106560250103588635885
-88388290488688259502502404430657258864530657253065030350602503035886355
-60250303506250303506035030358863506035030357025030357350325886357355032
+73503257250315886357250315665030656150335886356150335665703560657258863
-60257253065725306572588635886288290488761515140443065675886453065675532
+60353257025886353257025325735306573588635306573506025701560655735558863
+60657356035735603570258863560357025603560356065675886350602556655306567
+30656758863588628829048876151514044765031588645765032570650335701503355
+88635735033572503257250315886357250302573579570157958863570657957650302
+76503158863588628829088688250358825015024048890488188241502502515102025
-24048890881088688259502502404427501035886450125303506035303573501035885
+88635706501657650101577501158863577539570653757353658863570153355701531
+76526588635706520657252015665235886356652756065302563530158863501255302
+32530652015011588635206501065265017527501035886355886288298874887541021
+11088690388250108862078873588758838862880887588072058870103024882608821
+88758878875886883887588988260200886700020008867887404886882502200882505
+20300024044020002000103020036020088725008866882904887105188250300020002
+18825030020602009024048830881048871058824188250300020002001882503002302
+90240488308810488761008825790088253404887110002000200882410002000240705
+88658808869886545886588758873704887088108821887588788088758878874882905
-78875889887588737200360208862040498808828869886788698865908875887588904
+70244088758860470244088758875887886188675887886948875883885008060906960
+88758879708877420887588758878861886758875887958869886788508967777877780
+88758898875887370788758898875887372078838875889886988509599080078078976
+88758878808875886887588737078862880887588072887488586808059589700077780
-58865488582888288828882888288828882888288828882888202312258887777878878
(コンパイル方法)

というわけで、12回目の開催となる TeX & LaTeX Advent Calendar 2023 も、素敵なTeXネタを途絶えさせることなく無事にクリスマスの日を迎えられました。今年の参加者は全部で13名でした。参加者の皆様に心からの感謝を捧げたいと思います。

今年の重点テーマは「(La)TeXで幸せになる方法」でした。(La)TeXで何かが実現できればそれは当然幸せ😊になれます。そういうわけで比較的取り組みやすいテーマだったようで、重点テーマ絡みのネタが過去最高の19件1となりました😃

もしかしたら「TeXで幸せになるなんてケシカラン、TeXはやっぱり不幸😭を目指すべきである」という人も現れるかとチョット思っていたのですが、やっぱりクリスマス🎄の雰囲気に似合うのは不幸😭ではなく幸せ😊なのでしょう。トッテモ幸せ😊🤯なカレンダーができあがりました。

TeX & LaTeX Advent Calendar 2023 を楽しんでくれた皆さんに、

ありがとう!

そして


  1. 記事の文章の「本題に関係ある部分」が「幸せ」を含んでいるもの。

TeXでもTypstでもコンパイル可能なファイルを作りたい話

これは「TeX & LaTeX Advent Caleandar 2023」の12日めの記事です
(11日めは h20y6m さんでした。13日めは CareleSmith9 さんです)

結局12日目が埋まらなかったので、某ZRの小ネタ話になります(ざんねん🙃)

SATySFi以外の話

2021年のアドベントカレンダーTeX/LaTeXSATySFiの両埋め)にこんなネタがありました。

今回はSATySFiでなくTypstでやってみようという話です。まあ技術🌲的に目新しいことは何もないわけですが。

Typstを紹介する話

過去の某ツイーター記事をみましょう。

もっとマジメな説明を読みたいという人はこちらをどうぞ。

というわけで、今回はTeXとTypstのpolyglotをつくります。

TeXとTypstでいかにpolyglotするかの話

SATySFiのとき全く同じです。「LaTeXではどうやっても無理なので、plain TeXを使うことにして、強い裏技に頼る」ことにします。同じ方針で書くと、SATySFiのときとほぼ同様のものが完成するわけですが、さすがにそれでは退屈すぎるでしょう。

そこで今回は「なるべくコードを短くする」という条件を加える1ことにします。

TeXとTypstでpolyglotしてみた件

88バイトになりました。

Typst//\def~#1{\let~#1\def#1{\setbox0\box255\gdef#1{~}}}~\plainoutput\vfil\break\TeX\bye

plain TeX(例えばpdftex)でコンパイルしたときの出力。

TeXな出力

Typst2コンパイルしたときの出力。

Typstな出力

polyglotできてますね😃

まとめ

というわけで、枠が埋まらないとカレンダーがざんねん🙃になってしまうので、皆さん「TeX & LaTeX Advent Calendar 2023」にドンドン登録して幸せ😊になりましょう!💁


  1. その代わり、出力については「各処理系の既定のページ設定が適用された上で、本文領域に名前(“TeX”および“Typst”)だけが書かれていればよい」というルールにします。
  2. //はTypstの行コメントを開始する記法なので、Typstのコードとして見た場合は単に「Typst」と書いてあるだけです。“細工”は全てTeX側で行っています。