0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

徹底解説! LaTeXでナベアツなカウンタ出力命令をつくる話

0
Last updated at Posted at 2026-07-30

どうやら世間では再びナベアツが流行っている(いた?)ようです:wink:

日本の​(La)TeX界隈:sushi:ではナベアツは「3の倍数と3のつく数字だけフォントがアホ(CM Funny Italic)になる」という鉄板ネタになっています。

image-1.png

というわけで、せっかくだから、本記事では「ナベアツ的カウンタ出力命令」の実装について説明します。これ自体も過去に扱っているネタですが、今回は「イマドキのLaTeX機能実装の常識」に則した形で解説を進めます。フォント指定のような「原理的に完全展開可能にできない」ような要件をもつ一般のカウンタ命令実装にも応用できる話です。

必要な前提知識

  • フツーのTeX言語の知識:upside_down:
    • 特に「完全展開可能とは何か」について知っていること:upside_down::upside_down::upside_down:
  • フツーの「LaTeXのカウンタ出力命令\arabic等)の使用」に関する知識。
  • フツーのナベアツの知識(えっ:astonished:

ナベアツ的カウンタ出力命令を要件定義する話

カウンタ出力命令」というのは\arabic\Alphのように「LaTeXのカウンタの値を所定の番号書式で出力するための命令」のことでした。従って「ナベアツ的カウンタ出力命令」は「カウンタ値を算用数字で出力するが、3の倍数と3のつく数字だけアホなフォントで出力する」ような命令となります。命令名を\nabeazzstyleとすると、例えば次のように動作することが求められます。

\newcounter{foo}
% 39を"ナベアツ的"に出力する
\setcounter{foo}{39}\nabeazzstyle{foo}
% 40を"ナベアツ的"に出力する
\setcounter{foo}{40}\nabeazzstyle{foo}

image-2.png

もちろん\nabeazzstyleはカウンタ出力命令の一種なので、「カウンタ出力命令がサポートしている操作」は全てサポートしないといけません。特に重要なのが「相互参照での参照先カウンタ番号の出力」です。

example1.tex
% uplatex+dvipdfmx
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
%↓ここで"\nabeazzstyle"命令が定義されているとする
\usepackage{nazstyle}
% 1段目の箇条書きの番号をナベアツ的にする
\renewcommand*\theenumi{\nabeazzstyle{enumi}}
\begin{document}
{\LaTeX}の基本的なパッケージには以下のものがある。
\begin{enumerate}
\item graphicx
\item hyperref
% ↓箇条書き項目に参照ラベルを付けた
\item tikzducks\label{quack}
\end{enumerate}
% ↓相互参照してみた
この中で特に\ref{quack}は重要。
\end{document}

この例では箇条書きの番号を「ナベアツ的」に出力した上で、3番目の項目にラベルquackを付与しています。このラベルの数値は「3」なので、番号は「アホなフォントの『3』」になります。従って、その後で\ref{quack}でラベルを参照したときの番号出力も「アホなフォントの『3』」であるべきです。

image-3.png

ナベアツ的カウンタ出力命令が難しい話

TeX on LaTeX界隈において「カウンタ出力命令の実装」は有名な難題です。カウンタの相互参照は「現在のカウンタ番号を補助ファイル(*.aux)に書き出す」作業を伴うため、カウンタ出力命令の実装は頑強(robust)であることが求められます。その一方で「現在の値」への展開も必要なため「全体を保護付(protected)にする1」わけにはいきません。この相反する要件がカウンタ出力命令の実装を難しいものにしています。

この難題を解決する方策として、イマドキは「カウンタ出力命令は完全展開可能なマクロとして実装する」ことが常識になっています。完全展開可能であれば保護無しでそのまま頑強になり2両方の要件を自明に満たせるからです。例えばLaTeX標準のカウンタ出力命令の\Romanは完全展開可能なマクロとして実装されています。

\setcounter{foo}{42}
% '\Roman{foo}'を完全展開可能した結果が"XLII"になる
\edef\Test{\Roman{foo}}
\show\Test %==> "XLII"

一昔前のTeX言語では展開限定文脈で使用可能な機能がかなり限られていたため、完全展開可能実装そのものが困難なものでした。しかし今では各種拡張プリミティブやexpl3(およびLuaTeXのLua処理系)が利用できるため完全展開可能実装の難易度はかなり下がっています。このため「展開タイミングを見計らった細工」を考えるよりもロジック的に明快な完全展開可能実装の方がイマドキは好まれるわけです。

ところが本記事の目標である「ナベアツ的カウンタ出力命令」に関しては「イマドキの常識」である完全展開可能実装が使えません。ナベアツ的出力では数字のフォントを指定する必要があり、フォント指定はそれ自体がLaTeXの命令であるので完全展開の結果のトークン列として含められないからです。このような「要求仕様にLaTeXの命令が含まれる」ようなカウンタ出力命令ではどうしても「細工」が避けられず、その実装は依然として困難なのです。

これを踏まえて本記事では、「ナベアツ的カウンタ出力命令」を例にして完全展開可能実装に依拠せずにカウンタ出力命令を確実に実装するための手順を示すことにします。

ナベアツ的カウンタ出力命令をつくる話

改めて「ナベアツ的カウンタ出力命令」の要件を以下のように整理します。

  • アホになる場合はフォントを“CM Funny Italic”にした上で文字を125%に拡大する。
  • 命令名は\nabeazzstyleとする。
  • パッケージ名はnazstyleとし、名前空間接頭辞(とexpl3のモジュール名)もnazstyleとする。
  • TeX言語はアレ:sushi:なのでexpl3のコードで実装する:smiley:

手順1:数値出力用のマクロを実装する

まずは「整数値を入力して仕様通りの番号書式で出力する」ようなマクロ(本記事では数値出力マクロと呼びます)を、TeX言語でもexpl3でも何でもよいので実装しましょう。完全展開可能である必要はなく、また「数字表記の整数値」にだけ対応すれば十分です。

ただし一つ注意があり、このマクロは保護付(protected)で定義する必要があります。従って、マクロの定義は以下のようなパターンになります。

expl3の場合
% #1 は整数の十進表記
\cs_new_protected:Npn \‹数値出力マクロ› #1
  { ... }
TeX言語の場合
\protected\def\‹数値出力マクロ›#1{%
  ...
}

expl3を利用する場合の注意として、数値出力マクロを指す制御綴は「expl3仕様の関数名」ではなく「普通のLaTeX(\makeatletter状態)で使える名前3」にする必要4があります。従って、マクロ(関数)を定義する際には\cs_new:Nnではなく\cs_new:Npn(パラメタテキストを明示する書式)を使うことになります。

ここではナベアツ的な数値出力マクロを\nazstyle@printという名前で実装します。expl3を使えば実装の中身は極めて明快なので解説は省略します:upside_down:

\RequirePackage { graphicx }

%% アホなフォントの設定
\tl_const:Nn \l__nazstyle_aho_font_tl
  { \usefont { OT1 } { cmfr } { m } { it } }
%% アホなフォントで出力する.
\cs_new:Nn \nazstyle__aho_print:n
  { \scalebox { 1.25 } { \l__nazstyle_aho_font_tl #1 } }
%% ナベアツ的に数字を出力する.
\cs_new_protected:Npn \nazstyle@print #1
  {
    \exp_args:Ne \str_if_in:nnTF {#1} { 3 } % 3がつく数字
      { \nazstyle__aho_print:n {#1} } % アホ
      {
        \int_compare:nNnTF { \int_mod:nn {#1} { 3 } } = { 0 } % 3の倍数
          { \nazstyle__aho_print:n {#1} } % アホ
          {#1} % 非アホ
      }
  }
TeX言語での\nazstyle@printの実装

もし\nazstyle@printをTeX言語で実装するならこんな感じになるでしょう。

\RequirePackage{graphicx}

%% 変数
\newif\ifnazstyle@ok

%% アホなフォントの設定
\def\nazstyle@aho@font{%
  \usefont{OT1}{cmfr}{m}{it}%
}
%%アホなフォントで出力する.
\def\nazstyle@aho@print#1{%
  \scalebox{1.25}{\nazstyle@aho@font #1}%
}
%% 3がつく数であるか.
\def\nazstyle@check@three#1{%
  \nazstyle@okfalse
  \nazstyle@check@three@a#1!3\relax
}
\def\nazstyle@check@three@a#13#2\relax{%
  \ifx @#2@\else\nazstyle@oktrue\fi
}
%%ナベアツ的に数字を出力する.
\protected\def\nazstyle@print#1{%
  \nazstyle@check@three{#1}\ifnazstyle@ok % 3がつく数字
    \nazstyle@aho@print{#1}% アホ
  \else\ifnum\numexpr#1-#1/3*3=0 % 3の倍数
    \nazstyle@aho@print{#1}% アホ
  \else #1% 非アホ
  \fi\fi
}

手順2:カウンタ出力命令を定義する

数値出力マクロが実装できたら、次はいよいよ目的のカウンタ出力命令を実装します。この作業は簡単で、以下のような定型パターンのコードを書くだけです。

expl3の場合
\NewExpandableDocumentCommand \‹カウンタ出力命令› { m }
  { \use:e { \‹数値出力マクロ› { \arabic {#1} } } }
TeX言語の場合
\NewExpandableDocumentCommand\‹カウンタ出力命令›{m}{%
  \expanded{\‹数値出力マクロ›{\arabic{#1}}}%
}

数値出力マクロとは異なり、カウンタ出力命令は保護無しにする必要があります。従って、新方式のユーザ命令定義機能を使う場合は通常(保護付き)の\NewDocumentCommandではなく保護無し用の\NewExpandableDocumentCommandを使うことになります。

実をいうと、旧方式のユーザ命令定義(\newcommand)も保護無しの命令を定義するので、これを使ってもかまいません。

今回は、数値出力マクロ\nazstyle@printを基にしてカウンタ出力命令\nabeazzstyleを作りたいので、以下のようなコードを書きます。

\NewExpandableDocumentCommand \nabeazzstyle { m }
  { \use:e { \nazstyle@print { \arabic {#1} } } }

これで目的の「相互参照を正常に扱えるカウンタ出力命令」が完成しました。

手順3:内部カウンタ出力命令を定義する

カウンタ出力命令が完成したのですが、実はもう一つ定義しておいた方がよい(開発者レベル公開の)命令があります。それは以下のように定義される命令で、本記事では仮に「内部カウンタ出力命令」と呼ぶことにします。

expl3の場合
\NewExpandableDocumentCommand \@‹カウンタ出力命令名› { m }
  { \use:e { \‹数値出力マクロ› { \int_to_arabic:n {#1} } } }
TeX言語の場合
\NewExpandableDocumentCommand\@‹カウンタ出力命令名›{m}{%
  \expanded{\‹数値出力マクロ›{\number#1}}%
}

内部カウンタ出力命令はユーザが直接使うことはないのですが、以下の場面で実行されます。

  • \pagenumbering{nabeazzstyle}の実行により「ページ番号をナベアツ的」にする場合に内部で実行される。
  • 後述のenumitemサポートにおいて\AddEnumerateCounterのパラメタとして使用される。

この\pagenumberingの約束事があるため、内部カウンタ出力命令の制御綴名は\@‹カウンタ出力命令名›に固定されます。

ここでは以下のように\@nabeazzstyle命令を定義します。

\NewExpandableDocumentCommand \@nabeazzstyle { m }
  { \use:e { \nazstyle@print { \int_to_arabic:n {#1} } } }

手順4:enumitemパッケージのサポート

イマドキは箇条書きのレイアウト設定のためにenumitemパッケージを利用する人が増えています。このためenumitemパッケージのサポートを入れておくとよいでしょう。

\nabeazzstyleのような標準以外のカウンタ出力命令をenumitemで使えるようにするには、\AddEnumerateCounter命令でそのカウンタ出力命令を登録する必要があります。ユーザがこの登録作業をしなくて済むように、「本文開始時にenumitemパッケージが読込済の場合は自動で登録作業を行う」という処理を入れましょう。定型パターンとして書くと以下の通りです。

\AtBeginDocument{%
  \@ifpackageloaded{enumitem}{%
    \AddEnumerateCounter*{\‹カウンタ出力命令›}{\@‹カウンタ出力命令名›}{99}%
  }{}%
}

今回は以下のコードを書きました。

\AtBeginDocument{%
  \@ifpackageloaded{enumitem}{%
    \AddEnumerateCounter*{\nabeazzstyle}{\@nabeazzstyle}{99}%
  }{}%
}

ナベアツ的カウンタ出力命令をつくれた話

今までに挙げたコードをまとめることで、目標のnazstyleパッケージが完成しました。

nazstyle.sty(expl3版)
\ProvidesExplPackage {nazstyle} {2026-07-30} {0.3}
  {NabeAzz-style counter command}
\RequirePackage { graphicx, bxghost }

%% アホなフォントの設定
\tl_const:Nn \l__nazstyle_aho_font_tl
  { \usefont { OT1 } { cmfr } { m } { it } }
%% アホなフォントで出力する.
\cs_new:Nn \nazstyle__aho_print:n
  { \scalebox { 1.25 } { \l__nazstyle_aho_font_tl #1 } }
%% ナベアツ的に数字を出力する.
\cs_new_protected:Npn \nazstyle@print #1
  {
    \exp_args:Ne \str_if_in:nnTF {#1} { 3 } % 3がつく数字
      { \nazstyle__aho_print:n {#1} } % アホ
      {
        \int_compare:nNnTF { \int_mod:nn {#1} { 3 } } = { 0 } % 3の倍数
          { \nazstyle__aho_print:n {#1} } % アホ
          {#1} % 非アホ
      }
  }

%% カウンタ出力命令.
\NewExpandableDocumentCommand \nabeazzstyle { m }
  { \eghostguarded { \use:e { \nazstyle@print { \arabic {#1} } } } }
%% 内部カウンタ出力命令.
\NewExpandableDocumentCommand \@nabeazzstyle { m }
  { \use:e { \nazstyle@print { \int_to_arabic:n {#1} } } }

%% enumitemパッケージのサポート.
\AtBeginDocument
  {
    \@ifpackageloaded {enumitem}
      { \AddEnumerateCounter* \nabeazzstyle \@nabeazzstyle { 99 } }
      {}
  }
TeX言語版のnazstyle.styの実装
nazstyle.sty(TeX言語版)
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{nazstyle}[2026/07/30 v0.3 NabeAzz-style counter command]
\RequirePackage{graphicx,bxghost}

%% 変数
\newif\ifnazstyle@ok

%% アホなフォントの設定
\def\nazstyle@aho@font{%
  \usefont{OT1}{cmfr}{m}{it}%
}
%%アホなフォントで出力する.
\def\nazstyle@aho@print#1{%
  \scalebox{1.25}{\nazstyle@aho@font #1}%
}
%% 3がつく数であるか.
\def\nazstyle@check@three#1{%
  \nazstyle@okfalse
  \nazstyle@check@three@a#1!3\relax
}
\def\nazstyle@check@three@a#13#2\relax{%
  \ifx @#2@\else\nazstyle@oktrue\fi
}
%%ナベアツ的に数字を出力する.
\protected\def\nazstyle@print#1{%
  \nazstyle@check@three{#1}\ifnazstyle@ok % 3がつく数字
    \nazstyle@aho@print{#1}% アホ
  \else\ifnum\numexpr#1-#1/3*3=0 % 3の倍数
    \nazstyle@aho@print{#1}% アホ
  \else #1% 非アホ
  \fi\fi
}

%% カウンタ出力命令.
\NewExpandableDocumentCommand\nabeazzstyle{m}{%
  \eghostguarded{\expanded{\nazstyle@print{\arabic{#1}}}}%
}
%% 内部カウンタ出力命令.
\NewExpandableDocumentCommand\@nabeazzstyle{m}{%
  \expanded{\nazstyle@print{\number#1}}%
}

%% enumitemパッケージのサポート.
\AtBeginDocument{%
  \@ifpackageloaded{enumitem}{%
    \AddEnumerateCounter*{\nabeazzstyle}{\@nabeazzstyle}{99}%
  }{}%
}

実は上掲のnazstyleパッケージのコードでは\nabeazzstyle命令の実装にこっそりbxghostパッケージ\eghostguarded命令を導入しています。アホな数字の出力に\scaleboxを使っている関係で、そのままの実装では日本語の文書中で\nabeazzstyleを使った場合に適切な和欧文間空白(四分空き)が入らないことがあり、これを是正する必要があるためです。

パッケージが完成したので、相互参照を使った文書を用いて早速試してみましょう。本記事冒頭で掲載したexample1.texでもよい(もちろんこれも要件通りの出力が得られます:slight_smile:)ですが、せっかくenumitemに対応したので、enumitemと併用した例を試してみましょう。

通常のenumitemの仕様に従ってenumerate環境のオプションのlabelrefキーで\nabeazzstyle*と書くことで箇条書きの書式をナベアツ的に設定できます。

example2.tex
% uplatex+dvipdfmx
\documentclass[uplatex,dvipdfmx,a4paper]{jsarticle}
\usepackage{nazstyle,enumitem}% ナベアツ的カウンタ出力したい
\begin{document}
{\LaTeX}の基本的なパッケージには以下のものがある。
% 項目ラベルは"1."で参照時の番号は"1"とする.
\begin{enumerate}[label=\nabeazzstyle*., ref=\nabeazzstyle*]
\item graphicx
\item hyperref
\item tikzducks\label{quack}
\item enumitem\label{okay}
\end{enumerate}
この中で特に\ref{quack}\ref{okay}は重要。% 相互参照してみた
\end{document}

image-4.png

カンペキですね!:heart_eyes:

まとめ

というわけで、ナベアツの再ブームは一瞬で過ぎ去ってしまった模様:upside_down:ですが、「完全展開可能実装にできないカウンタ出力命令」が必要になったときは、ぜひともナベアツのことを思い出しましょう:information_desk_person:(えっ:astonished:

  1. つまり、\nabeazzstyle自体を保護付きマクロ(\protected\def)として定義するということです。

  2. 頑強(robust)とは「途中で保護付完全展開を挟んでも命令の仕様が満たされる」ということです。一方で完全展開可能とは「完全展開した結果がそのままマクロの仕様である」ということで、また「保護無しのマクロについては保護付完全展開は完全展開と同等」なので、完全展開可能なマクロで実装した命令は自明に頑強でもあるわけです。

  3. 数値出力マクロ自体はユーザレベルの公開命令ではないので普通は@を含む名前を付けます。expl3利用の場合、\ExplSyntaxOnだけでなく\makeatletterも有効である必要がありますが、普通に\ProvidesExplPackageを利用してパッケージを実装している場合はどちらも有効化されるはずです。

  4. その理由は、相互参照の処理において数値出力マクロの制御綴が補助ファイル(*.aux)に書き出されるからです。補助ファイルは\ExplSyntaxOnが無効の状態で読み込まれるため、expl3仕様の関数名の制御綴を認識できません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?