Hack & Learn

    クラスファイルの中味を眺めてみるdoronawa0.4.pdf, 73+iii ページ, 671 KB

※ 2009 年 10 月 2 日に doronawa.pdfdoronawa0.3.pdf に差し替えました。

※ 2009 年 10 月 3 日に再度 doronawa0.4.pdf に差し替えました…。orz...

■ 参考資料 :

plain.texlatex.tex をタイプセットする際には “\DoNotIndex” を使っていないため、インデックスがかなり冗長です (全部のコマンドをわざと拾ってみました)。なので、使いやすいドキュメントにするには、\DoNotIndex で余計なコマンドを削ってタイプセットし直したほうがいいです。


【追加】

2016 年 5 月から、従来の ASCII Corporation/ASCII MEDIA WORKS 版の pLaTeX の他に、日本語 TeX 開発コミュニティ (Japanese TeX Development Community) 版の pLaTeX が配布されるようになり、jclasses.dtx にも 10 年ぶりに修正が加えられています 〔参考〕。 現在の jclasses.dtx [2016/12/18 v1.7c] をタイプセットしたものココ に置いておきます iii + 62 p., 425 KB. しおり・変更履歴・コマンドインデックス付)

Added: February 2, 2017.


【追加】

こちらに引っ越してきた 2019 年 3 月現在の pldoc.tex をタイプセットしたものを、上の 「参考資料」 の部分に追加しました pldoc190208.pdf。 現在の jclasses.dtx [2018/10/25 v1.8a] も含まれています。 ただ、私の手元の LaTeX が古いため、2016 年以降に追加された機能についての説明部分が、正しく組めていない可能性があります (すいません)

Added: April 9, 2019.


Since: September 24, 2009.


【訂正】

16 ページの文献 [9] の書誌:

(誤) July 2009

(正) May 2008

【補足】

ここに置いてある source2e.tex をタイプセットしたファイルは 6 MB もありますが、CTAN: macros/latex/contrib/latex-tds/ にある Oberdiek さんが作成された pdf は 4 MB ほどです。

→ どうも何かが間違ってたみたいで、作り直しましたら、2.2 MB になりました…。ついでに plain.tex.pdflatex.tex.pdfclasses.dtx.pdf もタイプセットし直しました。 [Added: November 7, 2012.]

→ 手元の W32TeX [2012/08/30]$texmf/doc/latex/base/source2e.pdf は、目次・本文・変更履歴・索引の構成で、x + 581 p. (1.7 MB) でリンクなしなのですが、ここに置いている source2e.pdf は、同じ構成で ix + 541 p. (2.2 MB) で且つリンク付きなので、置いておく意味があると思っていたのですが (以前はそもそも W32TeX にはタイプセットされたものは入ってなかったと思いますし)TeXLive に収録されている source2e.pdf は同じ構成でリンクも付いてて、viii + 474 p. (2.3 MB) なのですね…。 [Added: November 11, 2012.]

また、2 ページで触れている tex.tex ですとか、errorlog.tex なんかも、ちゃんと Oberdiek さんがリンク付きの pdf にしてくださったものが CTAN のほうには置かれています。

Added: September 28, 2009.


【差し替え】

第 II 部のコードの行番号が “48” から始まっていました…。そのせいで、17 〜 19 ページでは行番号が 47 ずつズレてしまっていました。行番号が 1 から始まるように修正して、あと、参考文献のタイポを上掲のものも含めて計 3 箇所ほど直してタイプセットし直したファイルを doronawa0.2.pdf として、9 月 24 日版と差し替えました。昨年春にも似たようなミスをしていますが、申し訳ありません…。

Added: October 2, 2009.

他にも番号がズレていたので、再度差し替えました…(doronawa0.2.pdf → doronawa0.3.pdf)。[October 2, 2009, 9:50am]


【再々差し替え…】

行番号がズレていたのは 17 〜 19 ページだけではありませんでした…。うしろのほうも結構ボロボロとズレていました…。それで、ズレていた行番号を修正して doronawa0.4.pdf に差し替えました。

なんだか順調にバージョンが上がっているかのように見えてしまいますが、内容的な改善はまったくしていません。ひたすら行番号のミスを修正しているだけです…。

Added: October 3, 2009.


【訂正】

(誤) 10 ページ右段最終行: \newlenght → (正) \newlength

(誤) 11 ページ右段 5 行目: ことことが分かります → (正) ことが分かります

Added: October 5, 2009


【補足】

6 ページに

「これは飽くまで例ですので,“a” というコマンドを作っても意味はないのですけど,もしも本当に “a” というコマンドを作るのであれば,まず aactive にした後で,\def a{...} という感じに,コマンド a (バックスラッシュはつきません) の定義をすることになります」

と書きましたが、“A” というコマンドを作る面白い例を見つけたので、紹介しておきます (配布元は ココ ):

%%% David Carlisle (proposed by Frank Mittelbach): Guess what...
\month=10
\let~\catcode~`76~`A13~`F1~`j00~`P2jdefA71F~`7113jdefPALLF
PA''FwPA;;FPAZZFLaLPA//71F71iPAHHFLPAzzFenPASSFthP;A$$FevP
A@@FfPARR717273F737271P;ADDFRgniPAWW71FPATTFvePA**FstRsamP
AGGFRruoPAqq71.72.F717271PAYY7172F727171PA??Fi*LmPA&&71jfi
Fjfi71PAVVFjbigskipRPWGAUU71727374 75,76Fjpar71727375Djifx
:76jelse&U76jfiPLAKK7172F71l7271PAXX71FVLnOSeL71SLRyadR@oL
RrhC?yLRurtKFeLPFovPgaTLtReRomL;PABB71 72,73:Fjif.73.jelse
B73:jfiXF71PU71 72,73:PWs;AMM71F71diPAJJFRdriPAQQFRsreLPAI
I71Fo71dPA!!FRgiePBt'el@ lTLqdrYmu.Q.,Ke;vz vzLqpip.Q.,tz;
;Lql.IrsZ.eap,qn.i. i.eLlMaesLdRcna,;!;h htLqm.MRasZ.ilk,%
s$;z zLqs'.ansZ.Ymi,/sx ;LYegseZRyal,@i;@ TLRlogdLrDsW,@;G
LcYlaDLbJsW,SWXJW ree @rzchLhzsW,;WERcesInW qt.'oL.Rtrul;e
doTsW,Wk;Rri@stW aHAHHFndZPpqar.tridgeLinZpe.LtYer.W,:jbye

これを LaTeX ではなくて TeX で処理すると、驚きの結果になります。

タネ明かしをしてしまいますと、

\catcode`A13

\defA#1{\catcode`#113\def}

と定義して、この “A” というコマンドを使って文字を次々にアクティブにして置き換えをしています。ちゃんと最後までは読み解いていないのですけど、例えば、

AXX71FVLnOSeL71SLRyadR@oL

RrhC?yLRurtKFeLPFovPgaTLtReRomL;P

という部分が、なんと

\defX#1{\bigskip On{ }the{ }#1th{ }day{ }of{ }Christmas{ }my{ }true love gave{ }to{}me}

へと置換されます。

Added: October 8, 2009.


【追記】

こんなページに リンクを張ってくださった ようですので、ちょっと追記をしておきます。2009 年にこの 【補足】 を書いた際には、GUST の “TeX Pearls” というページ の 2005 年のものを 「配布元」 としましたが、このコードは 元々 1998 年に発表されたもの らしく、“xii.tex” というファイル名で CTAN: macros/plain/contrib/misc/xii.tex にもあります。“Christmas silliness” なんて説明されてますね。

Added: January 11, 2012.


【訂正】

(誤) 57 ページ 2 行目: “enumvi” → (正) “enumiv

(誤) 64 ページ欄外: \c@topdepth → (正) \c@tocdepth

【補足】

最近つくったある文書で、目次に出す項目のレベルを調整しようと思って、それで、ちゃんとクラスファイルの中の設定を確認するか、それか \addtocounter でも使えばよかったんですけど、最初、“\thetocdepth” としてデフォルトの値を見てみようとしたところ、“! Undefined control sequence.” といわれてしまい、ちょっと戸惑いました。それで、ltsect.dtx を見てみましたら、“tocdepth” とか “secnumdepth” といったカウンタは、LaTeX の \newcounter じゃなくて、plain TeX 以来の \newcount で作られているのですね…:

\newcount\c@secnumdepth

\newcount\c@tocdepth

\newcounter であれば、\the... という出力用のコマンドも一緒に作られますけど、\newcount で作られたパラメタの場合には、その値を出力するには \the\c@tocdepth としなくちゃいけなかったわけです。でもこれは気付きにくいんじゃないかなぁ。

Added: December 4, 2009.


【補足】

2 ページに、

「TeX の実行ファイルが tex.exe であるのかどうか (裸の TeX が tex.exe かどうか) も,ホントはかなり怪しいです。」

と書きましたが、ZR さんの En toi Pythmeni tes TeXnopoleos [電脳世界の奥底にて] に、次のように書いてありした:

大文字交じりの「TeX」や「LaTeX」は「ソフトウェアの名称」であり、小文字の「tex」や「latex」は「コマンド名」である(前者は所謂「ロゴ」で書かれることもある)。例えば、LaTeX を起動するコマンドが latex であり、 pLaTeX を起動するコマンドが platex である。この辺りは、基本的に「Perl と perl」、「Ruby と ruby」の関係と同じである。

 

ただし、「tex」については少し注意が必要で、正確には「TeX のコマンド名が tex」ではなく「plain TeX のコマンド名が tex」というのが正しい。同じように、「plain pTeX のコマンド名が ptex」であり、また pdftex、xetex、luatex 等についても同様である。

 

参考: TeX は組版用言語(組版エンジン)の名であり、「実際に起動されるもの(ソフトウェア)」は「TeX を用いて組み立てられた組版ソフトウェア(LaTeX、plain TeX、等)」であると考えられる。このことを「LaTeX は TeX の一種である」と呼ぶのであれば(そうするのが通例)、 tex も latex も「TeX の起動コマンド名」だということになる。

Added: October 14, 2010.


【感想】

1 年以上放っておいたページですが、あべのりさんの日記 の 「\aと\bを一回展開してマクロ\xに引数として渡すマクロはどう書くのがよいのだろう」 が面白かったので、ちょっとだけ感想 (というかただのコピペ) を。

\expandafter\expandafter\expandafter\x\expandafter{\expandafter\a\expandafter}\expandafter{\b}

のようにしたのでは、引数がもっと多いときには大変なことになる、というのは、そうですよねぇ。

それに対して kmaeda さん翌日、LaTeX3 の実装を元に (ひゃぁ!)

\def\id#1{#1}
\def\argnext#1#2#3{#2\id{#3{#1}}}
\def\exponce#1\id#2#3{\expandafter\argnext\expandafter{#3}{#1}{#2}}

\def\argoo{\exponce\exponce\id}
\def\argooo{\exponce\exponce\exponce\id}

\def\hogeii#1#2{\string#1\string#2}
\def\hogeoo#1#2{\argoo\hogeii{#1}{#2}} % #1, #2 を1回展開して \hogeii に渡す.
\def\hogeiii#1#2#3{\string#1\string#2\string#3}
\def\hogeooo#1#2#3{\argooo\hogeiii{#1}{#2}{#3}} % #1, #2, #3 を1回展開して \hogeiii に渡す.

\def\a{\b}
\def\b{b}

\hogeii\a\a
\hogeoo\a\a

\hogeiii\a\a\a
\hogeooo\a\a\a 

というやり方を提示されていて、展開の順番を変える勉強になるなぁ、と思いながら見てました。

他方、この時点でちょこっとググッてみたところでは (他力本願 ^^;expl3.sty を直接使う方法もヒットしたのですけど、他に、

なんかをヒントに “トークンリストレジスタ” が使えそう、と推測してちょっと試したりとかはしていたのですが、次の日に見事に ZR さん が、

% \xx@g@arglist の末尾に「{#1の1回展開}」を加える
\def\xx@addto@arglist#1{%
\begingroup
\toks\z@\expandafter{\xx@g@arglist}\toks\tw@\expandafter{#1}%
\xdef\xx@g@arglist{\the\toks\z@{\the\toks\tw@}}%
\endgroup
}
% #2〜#5の1回展開を引数にして#1を呼び出す
\def\xx@with@args@once@expanded#1#2#3#4#5{%
\global\let\xx@g@arglist\empty
\xx@addto@arglist{#2}\xx@addto@arglist{#3}%
\xx@addto@arglist{#4}\xx@addto@arglist{#5}%
\expandafter#1\xx@g@arglist
}
%----------------- テスト
% \xFoo 等はわざと未定義にしてある
\def\Foo{\xFoo}\def\Bar{\xBar}\def\Baz{\xBaz}\def\Quux{\xQuux}
\def\showArgs#1#2#3#4{\def\theInfo{args:{#1}{#2}{#3}{#4}}\show\theInfo}
\xx@with@args@once@expanded\showArgs\Foo\Bar\Baz\Quux

という回答をされていて、あー、こうやればよかったんだ、と納得。

(あと、ググった際には e-TeX の場合のやり方もヒットしたのですけど、それについてもちゃんと ZR さんは例を示されていて、まったくもって至れり尽くせり)

トークンリストレジスタなんて、私のような初級ではほとんど手を付けないものですけれど、マクロの展開の制御にトークンリストレジスタを利用する方法については、

というごく最近のドキュメントが参考になりました。この文書、

CTAN: graphics/pgf/contrib/pgfplots/doc/latex/pgfplots/TeX-programming-notes.pdf

というなんとも深いところに置かれています。

(Jan 16, 2012 追記: http://pgfplots.sourceforge.net/TeX-programming-notes.pdf にもありました)

Added: January 12, 2012.


【追記】

「マクロの展開の制御にトークンリストレジスタを利用する方法」 は TeXbook にも書いてありました (esp., EXERCISE 20.15) ので、そこの部分をこっそりコピペしておきます:

The TeXbook, pp. 216 & 330.

Expanded definitions that are made with \edef or \xdef continue to expand tokens until only unexpandable tokens remain, except that token lists produced by `\the' are not expanded further. Furthermore a token following `\noexpand' will not be expanded, since its ability to expand has been nullified. These two operations can be used to control what gets expanded and what doesn't.

Suppose, for example, that you want to define \a to be equal to \b (expanded) followed by \c (not expanded) followed by \d (expanded), assuming that \b and \d are simple macros without parameters. There are two easy ways to do it:

\edef\a{\b\noexpand\c\d}

\toks0={\c} \edef\a{\b\the\toks0 \d}

And it's even possible to achieve the same effect without using either \noexpand or \the; a reader who wants to learn more about TeX's expansion mechanism is encouraged to try the next three exercises.

EXERCISE 20.14

Figure out a way to define \a as in the previous paragraph, without using TeX's primitives `\noexpand' and `\the'.

20.14.

One idea is to say

\let\save=\c \let\c=0 \edef\a{\b\c\d} \let\c=\save

because control sequences equivalent to characters are not expandable. However, this doesn't expand occurrences of \c that might be present in the expansions of \b and \d. Another way, which is free of this defect, is

\edef\next#1#2{\def#1{\b#2\d}} \next\a\c

(and it's worth a close look!).

EXERCISE 20.15

Continuing the example of expansion avoidance, suppose that you want to expand \b completely until only unexpandable tokens are left, but you don't want to expand \c at all, and you want to expand \d only one level. For example, after \def\b{\c\c} and \def\c{*} and \def\d{\b\c} the goal would be to get the effect of \def\a{**\c\b\c}. How can such a partial expansion be achieved, using \the?

20.15.

\toks0={\c} \toks2=\expandafter{\d}

\edef\a{\b\the\toks0 \the\toks2 }

(Notice that \expandafter expands the token after the left brace here.)

〔EXERCISE 20.16 は割愛〕

あと、Appendix D: Dirty Tricks の最初のトピックが “Macro madness.” で、こちらも参考になりました (Appendix D なんて難しすぎてこれまで見てませんでした….ちなみに 6 番目のトピックの “Box maneuvers.” のところに、例の “dragon curve” が出てくるのですね (p. 391).PGF/TikZ には既に “lindenmayersystems” というライブラリが用意されているというのもスゴイ話だ….PGF/TikZ のマニュアルの当該ページで言及されてる The Algorithmic Beauty of Plants ってやつもスゴイですね)

Added: January 16, 2012.


【追記】

鹿野桂一郎さんという方のブログ (k16's note) の 2011/08/10 の “TeXで「最後のコマンド」だけ挙動を変える(ワンパス版)” という記事に、

いつもは文字列「hoge」を出力し、最後の出現だけは文字列「fuga」を出力するコマンド\hogeの定義はこれだけです。

\long\def\hoge#1\hoge#2{%

  \ifx#2\end fuga#1#2%

    \else hoge#1\expandafter\hoge#2

  \fi}

こんなふうに使います。pdftexを起動してインタプリタに以下を貼り付ければ、最後の\hogeだけがfugaに置き換わったPDFができあがります。

\hoge \hoge $1+2=3$ \hoge

\hoge \hoge

oshimai

% 本文はここまで

\hoge\end % この行を本文の最後に追記する

\eject

と書かれているのを拝見して、試してみましたところ、TeX から、

! Extra \else.

と言われてしまいました。 そこで、\tracingmacros で追ってみましたら、

\hoge #1\hoge #2->\ifx #2\end fuga#1#2\else hoge#1\expandafter \hoge #2 \fi

#1<-

#2<-$

 

\hoge #1\hoge #2->\ifx #2\end fuga#1#2\else hoge#1\expandafter \hoge #2 \fi

#1<-$ \fi 1+2=3$

#2<-\hoge

 

(中略)

 

\hoge #1\hoge #2->\ifx #2\end fuga#1#2\else hoge#1\expandafter \hoge #2 \fi

#1<-\else hoge$ \fi 1+2=3$ \expandafter

#2<-\hoge

 

\hoge #1\hoge #2->\ifx #2\end fuga#1#2\else hoge#1\expandafter \hoge #2 \fi

#1<-hoge\else hoge$ \fi 1+2=3$ \expandafter \expandafter

#2<-\hoge

 

(以下略)

とかとなっていて、\fi \else が残ったまま #1 に入ってしまっていました。\else とか \fi なんていうのは、true か false かが決まればその途端に消えてしまうのかと思ってましたが、どうも違うようです。

この点については TeX by Topic の “13.8.3 Macros and conditionals; the use of \expandafter” が参考になりましたので、またまたこっそりコピペしちゃいます:

Consider the following example:

\def\bold#1{{\bf #1}} \def\slant#1{{\sl #1}}

\ifnum1>0 \bold \else \slant \fi {some text} ...

This will make not only ‘some text’, but all subsequent text bold. Also, at the end of the job there will be a notice that ‘end occurred inside a group at level 1’. Switching on \tracingmacros reveals that the argument of \bold was \else. This means that, after expansion of \bold, the input stream looked like

\ifnum1>0 {\bf \else }\fi {some text} rest of the text

so the closing brace was skipped as part of the <false text>. Effectively, then, the resulting stream is

{\bf {some text} rest of the text

which is unbalanced.

One solution to this sort of problem would be to write

\ifnum1>0 \let\next=\bold \else \let\next=\slant \fi \next

but a solution using \expandafter is also possible:

\ifnum1>0 \expandafter \bold \else \expandafter \slant \fi

This works, because the \expandafter commands let TeX determine the boundaries of the <true text> and the <false text>.

In fact, the second solution may be preferred over the first, since conditionals are handled by the expansion processor, and the \let statements are tackled only by the execution processor; that is, they are not expandable. Thus the second solution will (and the first will not) work, for instance, inside an \edef.

そこで、まず、上の一つ目の対処法に倣うとすると、例えば、次のようにして #2\fi の外に出しちゃえばいいのかも知れません (“\end” は LaTeX のコマンドにあるので、 “\stophoge” に替えました)

\long\def\hoge#1\hoge#2{%

  \ifx#2\stophoge

     hoge #1 fuga\let\stophoge\relax%

  \else

     hoge #1\expandafter\hoge

  \fi#2}

でも、これだと \expandafter だけじゃなく \let も使っているので、実際には一つ目と二つ目の対処法のミックスですね…。それに、二つ目の方法のほうが preferable とのことなので、

\long\def\hoge#1\hoge#2{%

   \ifx#2\stophoge

      hoge #1 fuga%

   \else

      hoge #1\expandafter\hoge\expandafter#2\fi}

とすれば、\expandafter だけで \hoge#2\fi の外に出せて、純粋に二つ目の対処法といえるでしょうか。なお、\fi の外に出したいトークンが沢山ある場合については、“13.8.6 A trick” に \hop というマクロが紹介されてましたので、これを使えば、

\def\hop#1\fi{\fi#1}

\long\def\hoge#1\hoge#2{%

   \ifx#2\stophoge

      hoge #1 fuga%

   \else

      hoge #1\hop\hoge#2%

   \fi}

とすることも可能かと思います (なんと、\fi をデリミタに使うだなんて!)

もちろん、鹿野さんはちゃんと分かってらして単純に \expandafter を書き落とされただけだったのでしょうし、鹿野さんのブログを読まれるようなみなさんは即座に 「脳内変換 (補完)」 してしまわれたのでしょうけれど、私はお蔭で勉強になりました。

Added: January 27, 2012.


【追記の追記】

鹿野さんのブログの 当該ページ が 2012/2/27 に更新されてたことに今日気付きました…。お手数をお掛けしてしまいました。

Added: March 12, 2012.


【追記】

53頁以降の 「6.3 リスト環境」 の部分には、リスト環境一般についての説明がスッポリと抜けてしまっていたなぁ、と今更ながら思えて来たので、リスト環境について、少し補足 (というか、相変わらずほとんどコピペですが) をしてみようかと思います (リスト環境は、「8.2 参考文献」 の thebibliography 環境 (69頁) の中でも使われています)

53頁には、まず、リスト環境のパラメタの図を載せて、その次に jclasses.dtx から、

 リスト環境のデフォルトは次のように設定されます。
 まず,\rigtmargin [sic]\listparindent\itemindent をゼロにします。そして,K 番目のレベルのリストは \@listK で示されるマクロが呼び出されます。ここで ‘K’ は小文字のローマ数字で示されます。たとえば,3 番目のレベルのリストとして \@listiii が呼び出されます。 \@listK\leftmargin\leftmarginK に設定します。

という部分を引用しましたが、この “\@listK” というパラメタのセットが実際どこで呼ばれるのか、には言及してませんでしたし、57頁の 「6.3.1 enumerate 環境」 の部分では、

\usecounter\makelabel は LaTeX のマニュアルにも載ってますけど,\list 環境のデフォルトの挙動を変更するときに使うものらしいです。

と言っただけだったのも、不親切だったように思います。

それで、まずは、その LaTeX のマニュアルのレファレンス部分から list 環境の概要を見てみましょう:

Appendix C Reference Manual
C.6 Displayed Paragraphs
C.6.3 The list and trivlist Environments

\begin{list}{default_label}{decls} item_list \end{list}

Produces a list of labeled items.

In addition to declarations that set these parameters, the following declaration may appear in decls:

ふむふむ、なるほど trivlist の部分は割愛しました)

次に ltlists.dtx から関係しそうな部分のコードを拾ってみますと、

\def\list#1#2{%
  \ifnum \@listdepth >5\relax
    \@toodeep
  \else
    \global\advance\@listdepth\@ne
  \fi
  \rightmargin\z@
  \listparindent\z@
  \itemindent\z@
  \csname @list\romannumeral\the\@listdepth\endcsname
  \def\@itemlabel{#1}%
  \let\makelabel\@mklab
  \@nmbrlistfalse
  #2\relax
  \@trivlist
  \parskip\parsep
  \parindent\listparindent
  \advance\linewidth -\rightmargin
  \advance\linewidth -\leftmargin
  \advance\@totalleftmargin \leftmargin
  \parshape \@ne \@totalleftmargin \linewidth
  \ignorespaces}

\def\@mklab#1{\hfil #1}

\def\item{%
  \@inmatherr\item
  \@ifnextchar [\@item{\@noitemargtrue \@item[\@itemlabel]}}

\def\@item[#1]{%
…中略…
  \sbox\@tempboxa{\makelabel{#1}}%
…中略…
  \ignorespaces}

のあたりかと思います。

ポイントは、list 環境の第一引数が \@itemlabel に格納されるという点と、\@listdepth の深さに応じて “\csname @list\romannumeral\the\@listdepth\endcsname” の部分が \@listi\@listvi になるということ、あと、この \@listK\let\makelabel\@mklab よりも後ろに第二引数が置かれているということ、でしょうか。

つまり、第二引数でパラメタの値を変更しなければ、\@listK の中味のデフォルトの値が使われ、また、第二引数で \makelabel を変更しなければ、第一引数が \@mklab の引数となって、ボックスの中で右寄せにされるということが分かります。

逆に、デフォルトのパラメタの値や、ラベルの出力形式を変更したい場合には、list 環境の第二引数でパラメタに値を代入して上書きしたり、\makelabel を再定義すればよい、ということになります。

\@listi では、\leftmargin\parsep\topsep\itemsep が設定されていますが、このうち、\parsep\itemsep の値は list 環境に入ってからでも変更可能ですが、\leftmargin\topseplist 環境に入ってから変更することは出来ません。list 環境に入った後で \leftmargin の値が変更出来ないというのは \list の定義を見るだけでも明らかですが、\topsep のほうは \@trivlist の中で使われているために、list 環境に入った後で値を変更しても、その変更を反映させることが出来ません。

 \leftmargin\topsep の値を変更するには、\parshape\@trivlist よりも前じゃないといけないわけで、そうすると、\@listK の中か、list 環境の第二引数において、ということになります (*註1)。素直には、\@listK を再定義して値を代入するか、list 環境の第二引数で値を代入することになるのでしょうけれど、ここで \g@addto@macro あたりを利用することも可能なように思えます(*註2)。

 つまり、まず、list 環境に入ってからでも変更可能なパラメタについては (ブリアンブル等で)
   \g@addto@macro{\itemize}{\itemsep0pt\parsep0pt\parskip0pt}
とか
   \g@addto@macro{\itemize}{\itemindent1zw\advance\labelsep\itemindent}
としてみたりですとか、また、例えば \@listi で設定されている値を変更するには (当該 list 環境の直前で)
   \g@addto@macro{\@listi}{\topsep0pt}
としたりなんかも出来そうな気がします \@listi\small\footnotesize 内でも定義されていて、また、\normalsize で元々の \@listi のコピーの \@listI に戻されているので、ブリアンブルでその時点での \@listi の値を上書きしても、\small 等々を使った後では変更を反映出来ません)。かなり場当たり的ですけれど。

 (*註1) \rightmargin\listparindent の値を変更出来るチャンスも同様に、\@listK の中か、list 環境の第二引数においてだということが、\list の定義から分かります。

 (*註2) 尤も、\leftmargin には、\@listK の中で \leftmarginK の値が代入されていて、\leftmarginK の値自体は \@listK の外で与えられているので、わざわざ \g@addto@macro を使って \@listK の中の \leftmargin の値を上書きしたりする必要はないですね。
  \labelsep\labelwidth\partopsep の値も、\@listK でも \list でもないところで設定されているので、これらの値も list 環境の外で変更することが可能です (正確には、\labelwidth の値は \@listK の中で計算されているのですが、\@listK の外で与えられている \leftmarginK の値に基づいているので、\leftmarginK を変更すれば変えられます)

list 環境のパラメタのデフォルトの値が設定されるタイミングをまとめてみますと、
 (1) まず、\list の外で cls ファイルや clo ファイルにおいて)
     ・ \leftmarginK
     ・ \labelsep, \labelwidth (=\leftmarginK-\labelsep), \partopsep
     ・ \@listK\leftmargin=\leftmarginK, \parsep, \topsep, \itemsep
 (2) そして、\list が実行される度にその中で:
     ・ \rightmargin, \listparindent, \itemindent
     ・ \@listK を展開 (→ \leftmargin=\leftmarginK, \parsep, \topsep, \itemsep
     ・ #2 〔← この第二引数でパラメタの値を上書きすることが可能〕
     ・ \@trivlist 実行 \topsep, \partopsep, \parskip=\parsep を使用)
     ・ \parshape 実行 \leftmargin\rightmargin を使用)
という感じでしょうか。

\itemindent について
 このパラメタは結構誤解されやすいと思うのですけれど、上で 「略」 としたレファレンスには、
   \itemindent: The indentation of the first line of an item. Its default value is zero in the standard document class. It may have a negative value.
と書いてありますが、ltlists.dtx のほうには、
   \itemindent: extra indentation added right BEFORE an item label.
と説明されています。
 53頁に掲載した図は、The LaTeX Companion, 2nd, p. 145 と同じものなのですが、ラベルが本文に食い込んでいる点に注意が必要です。
 つまり、\itemindent は、箇条書きのラベル付き項目本文の字下げ量というよりは、“\labelwidth + \labelsep” をまとめて左右にズラす量です (→ 別ページ\itemindent に正の値を設定すると、ラベルと項目本文との距離を \labelsep に保ったまま、ラベルが右に \itemindent の分移動します。なので、ラベルの位置を変えずにラベル付き項目本文の字下げをしたいという場合には、\itemindent を設定して、且つその分だけ \labelsep の値を増やさないといけません (ちなみに、\itemindent に負の値を設定している例としては、description 環境や verse 環境を挙げることが出来ます)

\usecounter の仕組みは割愛しちゃいますが、使い方はとても簡単です。“\usecounter” という名前だけ聞くと一般性がありそうに思えますが、このコマンドは list 環境の第二引数の中でだけ使うものです。既に用意してあるカウンタを、list 環境の第二引数の中で、このコマンドの引数にすると、当該 list 環境内で \item ごとにそのカウンタがインクリメントされ、また、相互参照も可能となる、というものです。

最後に、LaTeX のマニュアルの list 環境の説明本文を転記しておきます:

Chapter 6 Designing It Yourself
6.6 List-Making Environments
6.6.1 The list Environment

The list environment has two arguments. The first specifies how items should be labeled when no argument is given to the \item command; the second contains declarations to set the formatting parameters.

…中略…

The first argument of the environment is the text to be used as the label for any \item command with no optional argument. To number the items automatically, the second argument of the list environment should contain a \usecounter{ctr} command whose argument is the name of a counter---usually one defined with \newcounter. This counter is reset to zero at the beginning of the environment and is incremented by one before the execution of any \item command that has no optional argument, so it can be used to generate a label number.

----- sample code -----
\begin{minipage}{225pt}
\newcounter{bean}
This sentence represetnts the end of the text that 
precedes the list.
\begin{list}
   {B--\Roman{bean}}
   {\usecounter{bean}
    \setlength{\rightmargin}{\leftmargin}}
 \item This is the first item of the list. Observe how the left and 
       right margins are indented by the same amount.
 \item This is the second item.
\end{list}
As usual, the following text starts a new paragraph only if 
the list environment is followed by a blank line.
\end{minipage}
----- sample code -----

Added: November 5, 2012.


【補足】

29 頁に、\@lowpenalty\@medpenalty\@highpenalty との関連で “\nopagebreak” の定義を転記しましたが、その際、

\@no@pgbk の “#1” って何でしょう?

と書いた疑問が解けたので、今更ながらちょっと補足を。

そこでは、ltspace.dtx から、以下の部分を引用しました:

\def\nopagebreak{\@testopt\@no@pgbk4}

\def\@no@pgbk #1[#2]{%
  \ifvmode
    \penalty #1\@getpen{#2}%
  \else
    \@bsphack
    \vadjust{\penalty #1\@getpen{#2}}%
    \@esphack
  \fi}

\def\@getpen#1{\ifcase #1 \z@ \or \@lowpenalty\or
         \@medpenalty \or \@highpenalty
         \else \@M \fi}

それで、当時はこの部分だけをじーっと睨んで、「\@no@pgbk の “#1” に相当するものが見当たらないよなぁ…。 並び的には \@getpen{#2}#1 倍してるみたいだけど、なんで定数倍してるんだろう?」 と思ってしまったわけですが、もう一行上の “\pagebreak” の定義も一緒に見れば、すぐに分かる話でした:

\def\pagebreak{\@testopt{\@no@pgbk-}4}

ね? \nopagebreak のときの \@no@pgbk の “#1” は確かに “空” で、そして、\pagebreak のときの \@no@pgbk の “#1” は “-” なのですね (“\@no@pgbk-” の部分は、“\@no@pgbk” と “-” の 2 トークン)! なーんだ。

ついでに LaTeX2.09 のときの latex.tex から \nopagebreak\pagebreak の定義も見ておけば、もっと早くこのことに気付けたのかも知れません:

\def\nopagebreak{\@ifnextchar[{\@nopgbk}{\@nopgbk[4]}}

\def\@nopgbk[#1]{\ifvmode \penalty \@getpen{#1}\else
\@bsphack\vadjust{\penalty \@getpen{#1}}\@esphack\fi}
 
\def\pagebreak{\@ifnextchar[{\@pgbk}{\@pgbk[4]}}

\def\@pgbk[#1]{\ifvmode \penalty -\@getpen{#1}\else
\@bsphack\vadjust{\penalty -\@getpen{#1}}\@esphack\fi}

つまり、LaTeX2.09 のときには “\@nopgbk” と “\@pgbk” の 2 つに分けて処理していたのを、LaTeX2e では “\@no@pgbk” ひとつにまとめたわけですね。

Added: April 15, 2013.


【追記】

Acetaminophen さんのブログ 「アセトアミノフェンの気ままな日常xcolor と color が難しい件(その1)xcolor と color が難しい件(その2) を拝読して学んだことを、自分でも簡単にまとめておこうと思います。

プリアンブルでは、同一のパッケージに対して複数回 \usepackage を使うことができるけれど、オプションを指定している場合には、その指定の仕方によっては、“option clash” というエラーになることがある、という話です。

(A) 同一パッケージに対して複数回 \usepackage を使ってもエラーにならない例

例えば、pkgA.sty というパッケージがあったとして、これが、foobarbaz というオプションをとれるとします (ここで、foobarbaz は、互いに干渉しないオプションだとします.つまり、draftfinal みたいに、一方を指定すると他方を指定できないようなオプションではないと考えます)

それで、プリアンブルで、

\usepackage[foo,bar,baz]{pkgA} % (1)
\usepackage{pkgA} % (2)
\usepackage[bar]{pkgA} % (3)
\usepackage[baz,foo]{pkgA} % (4)

のように書いたとしても、これはエラーにはなりません。

実は、pkgA.sty をロードしているのは (1) の \usepackage だけであって、(2) から (4) の \usepackage は、pkgA.sty を重複ロードしているわけではなく、(1) で指定されているオプションとの整合性チェックの働きしかしていません。

もう少ししつこく言いますと、「パッケージは、最初の \usepackage のときだけロードされる」 仕様であって、オプションも、その最初の \usepackage で指定されたものだけ (*註) が有効になります。同一パッケージに対する 2 回目以降の \usepackage は、パッケージを重ねてロードしているわけではなくて、 2 回目以降の \usepackage のオプションの指定が最初の \usepackage の指定と矛盾していないかのチェックだけをしている、ということです。

(*註) 実際に有効になるオプションは、当該パッケージに対して 「最初の \usepackage よりも前に \PassOptionsToPackage で指定されているオプション」 と、「最初の \usepackage で指定されているオプション」 とを合わせたもの、です。→ (C-1)、(E)参照

今の例では、(1) の時点で pkgA.sty がロードされていて、そして、オプションは、foobarbaz すべてが有効になっています。

そのため、(2) で、オプションなしにしてあったり、(3) で bar のみが指定してあったり、(4) では bazfoo が指定してあったりしても、いずれも (1) の “\usepackage[foo,bar,baz]{pkgA}” という指定と矛盾しないので、エラーとはなりません。

(B) エラーになる例 : 単純な場合

わざわざ繰り返すまでもないのですが、

\usepackage{pkgA}
\usepackage{pkgA}

とか、

\usepackage[bar]{pkgA}
\usepackage[bar]{pkgA}

さらには、

\usepackage[foo]{pkgA}
\usepackage{pkgA}

としてもやはり、いずれもエラーにはなりません。

つまり、これらはどれも、「最初の \usepackage のオプション指定」 と、「2 回目以降の \usepackage のオプション指定」 とが、矛盾していないからです。

「矛盾する ・ しない」 という表現は曖昧かも知れないので、もう少し丁寧に言いますと、オプションは最初の \usepackage で指定されたものだけが有効なわけで、エラーになるのは、2 回目以降の \usepackage において最初に指定されたオプション以外のオプションが指定されたとき、ということです。

以上から、例えば、

\usepackage{pkgA}
\usepackage[baz]{pkgA}

とか、

\usepackage[bar]{pkgA}
\usepackage[foo]{pkgA}

のように指定すると、“option clash”というエラーになります:

! LaTeX Error: Option clash for package pkgA.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return> for immediate help.

ここで、“?” というプロンプトに対して “h” をタイプしてヘルプを表示させますと、一つ目の場合は、

The package pkgA has already been loaded with options:
  []
There has now been an attempt to load it with options
  [baz]
Adding the global options:
  ,baz
to your \documentclass declaration may fix this.

と言われ、二つ目の場合は、

The package pkgA has already been loaded with options:
  [bar]
There has now been an attempt to load it with options
  [foo]
Adding the global options:
  ,foo
to your \documentclass declaration may fix this.

と表示されます。

「後ろの \usepackage で指定しているオプションを、\documentclass のグローバルオプションにすると解決するかも」 というアドバイスなのですが、でも、今の場合は pkgA が複数回 \usepackage されているのが目で見て分かりますので、修正はもっと単純に出来て、一つ目は、

\usepackage[baz]{pkgA}

とだけ書けばよく、二つ目は、

\usepackage[bar,foo]{pkgA}

と直せばいいだけです。

※ 「後ろの \usepackage で指定しているオプションを、クラスファイルのグローバルオプションにする」 という方法は、クラスファイルがそのオプションを持っていない場合 (ドライバオプションの場合など) には確かに有効な対処法なのですけれど、もしも、クラスファイルが baz とか foo に相当するオプションを持っていたりする場合には、意図してない副作用が生じる可能性があります。

(C-1) ちょっと気付きにくい例 : その 1

上述 (B) の場合は、同一パッケージに対して複数回 \usepackage が使われているのが簡単に確認できましたが、一見しては分からないような場合もあります。

例えば、pkgB.sty が内部で pkgA.sty\RequirePackge している場合、それを知らずに、ユーザーが pkgA に対して foo を与えようとして、

\usepackage{pkgB} % 内部で \RequirePackage{pkgA}
\usepackage[foo]{pkgA}

とすると、最初の \RequirePackage{pkgA} はオプションなしで、2 回目の \usepackage[foo]{pkgA} がオプション foo ありなので、エラーとなります。

この場合は、\usepackage の順番を逆にして、

\usepackage[foo]{pkgA}
\usepackage{pkgB} % 内部で \RequirePackage{pkgA}

とすれば、エラーを回避出来ます。

しかし、もしもここで、pkgB.sty が内部で \RequirePackage[bar]{pkgA} としていたら、どうでしょうか。ユーザーが、pkgA に対して foo というオプションを与えたい場合には、

\usepackage{pkgB} % 内部で \RequirePackage[bar]{pkgA}
\usepackage[foo]{pkgA}

としても、順番を入れ替えて、

\usepackage[foo]{pkgA}
\usepackage{pkgB} % 内部で \RequirePackage[bar]{pkgA}

としても、いずれもエラーになってしまいます。

この場合、option clash エラーのヘルプを参照して、pkgB が内部で \RequirePackage[bar]{pkgA} としていることが分かれば、\PassOptionsToPackage{OptionList}{Pkg} というコマンドを使うという手があります。

\PassOptionsToPackage を使うと、パッケージをロードする前に、当該パッケージにオプションを渡しておくことが出来ます。したがって、

\PassOptionsToPackage{foo}{pkgA}
\usepackage{pkgB} % 内部で \RequirePackage[bar]{pkgA}

とすると、pkgA がロードされる際に、foo bar 両方のオプションを有効にすることが出来ます。

\PassOptionsToPackage でパッケージにオプションを渡すには、当該パッケージが最初に \usepackage されるより前に、\PassOptionsToPackage を使わないといけません 。→ (E) 参照

※ ちなみに、困ったことに、もしも、foobar が、どちらか一方しか指定できないようなオプションであるような場合には、pkgB.sty を修正するしかありませんね…。

(C-2) ちょっと気付きにくい例 : その 2

(C-1) は、パッケージが別のパッケージを内部でロードしている場合でしたが、クラスファイルが内部でパッケージをロードしている場合もあります。

例えば、sample.cls が、内部で \RequirePackge{pkgA} としている場合には、pkgAfoo というオプションを与えたいとすると、

\documentclass{sample} % 内部で \RequirePackage{pkgA}
\usepackage[foo]{pkgA}

とするとエラーになり、

\documentclass{sample} % 内部で \RequirePackage{pkgA}
\PassOptionsToPackage{foo}{pkgA}

としてみると、これはエラーにこそなりませんが、pkgAfoo を渡すことは出来ません。 → (E) 参照

でも、\PassOptionsToPackage\documentclass より前でも使えるので、この場合は、

\PassOptionsToPackage{foo}{pkgA}
\documentclass{sample} % 内部で \RequirePackage{pkgA}

とすれば、めでたく pkgA にオプション foo を渡すことが出来るようになります。

※ ちなみに、クラスファイル内部で pkgA がロードされている場合には、foo\documentclass のオプションにすればよいのではないかと思われるかも知れませんが、内部で \RequirePackage{Pkg} とされてるだけですと、\documentclass のオプションは、Pkg には渡りません (クラスファイル内部でロードしているパッケージに \documentclass のオプションが渡るには、\RequirePackageWithOptions{Pkg} というコマンドが使われている必要があります)

(D) まとめ

以上から、“option clash” に遭遇した場合の対処法としては、

というやり方が考えられます。

三つ目の方法が簡単なように思われますが、(B) の末尾でも触れましたように、もしもそのクラスファイルが当該オプションを有しているような場合には、余計な副作用が起こる可能性もありますし、そもそもグローバルオプションは、ロードされているすべてのパッケージに渡されてしまいます。

そうしますと、対象のパッケージにピンポイントでオプションを渡すのに確実で安全な方法は、二つ目の \PassOptionsToPackage を使うやり方でしょうか。

(E) オマケ : \usepackage\PassOptionsToPackage

最後に、\usepackage\PassOptionsToPackage の関係について、その定義 \@pass@ptions\@onefilewithoptions を追って確認してみよう思います。

まず、\usepackage[OptionList]{Pkg} は、

そして、\PassOptionsToPackage{OptionList}{Pkg} は、

それで、2 回目以降の \usepackage の際に実行される

\@if@ptions\@currext{Pkg}{OptionList}{}{\@latex@error{...}{...}} 

は、まず、

\@if@pti@ns{\opt@Pkg.sty の展開結果}{OptionList}{}{\@latex@error{...}{...}} 

へと展開される。

これはさらに、“,OptionList,” と “,\opt@Pkg.sty の展開結果,” とを比較して、前者が後者の部分集合であれば、

\@firstoftwo{}{\@latex@error{...}{...}} 

へと展開され (つまり何も起こらない)、そうでなければ、

\@secondoftwo{}{\@latex@error{...}{...}} 

へと展開される (つまりエラー)

さて、以上を踏まえて、次の 3 つの場合について考えてみます:

(a) \usepackage のみ が 2 回使われた場合

(b) 最初の \usepackage より前に \PassOptionsToPackage が使われた場合

(c) 最初の \usepackage より後に \PassOptionsToPackage が使われた場合

ここで、\PassOptionsToPackage で与えるオプション群を “BeforeList”、最初の \usepackage で与えるオプション群を “FirstList”、2 回目の \usepackage で与えるオプション群を “SecondList” としますと、

(a) \usepackage のみ が 2 回使われた場合

\usepackage[FirstList]{Pkg} % (1)
\usepackage[SecondList]{Pkg} % (2)

この場合は、

(1) : \opt@Pkg.sty が作られる (中味は FirstList。この (1) で Pkg.sty はロードされる。有効になるオプションは、FirstList

(2) : ここでは Pkg.sty はロードされない。\opt@Pkg.sty (= FirstList) と SecondList とが比較され、SecondList\opt@Pkg.sty (の中味) の部分集合であれば、何も起こらない。そうでない場合には、option clash エラーとなる。

(b) 最初の \usepackage より前に \PassOptionsToPackage が使われた場合

\PassOptionsToPackage{BeforeList}{Pkg} % (3)
\usepackage[FirstList]{Pkg} % (4)
\usepackage[SecondList]{Pkg} % (5)

この場合は、

(3) : \opt@Pkg.sty が作られる (中味は BeforeList

(4) : \opt@Pkg.styFirstList が追加される。この (4) で Pkg.sty はロードされる。有効になるオプションは、BeforeList + FirstList

(5) : ここでは Pkg.sty はロードされない。\opt@Pkg.sty (= BeforeList + FirstList) と SecondList とが比較され、SecondList\opt@Pkg.sty (の中味) の部分集合であれば、何も起こらない。そうでない場合には、option clash エラーとなる。

(c) 最初の \usepackage より後に \PassOptionsToPackage が使われた場合

\usepackage[FirstList]{Pkg} % (6)
\PassOptionsToPackage{BeforeList}{Pkg} % (7)
\usepackage[SecondList]{Pkg} % (8)

この場合は、

(6) : \opt@Pkg.sty が作られる (中味は FirstList。この (6) で Pkg.sty はロードされる。有効になるオプションは、FirstList

(7) : \opt@Pkg.styBeforeList が追加される。

(8) : ここでは Pkg.sty はロードされない。\opt@Pkg.sty (= FirstList + BeforeList) と SecondList とが比較され、SecondList\opt@Pkg.sty (の中味) の部分集合であれば、何も起こらない。そうでない場合には、option clash エラーとなる。

ということは、(8) で指定するオプションを (7) で追加しておくと、option clash エラーが生じない、ということになります。そして、(8) では Pkg.sty はロードされないので、結局 (7) や (8) で指定したオプションが有効になることもありません。 (Acetaminophen さんが xcolor と color が難しい件(その2) の最後でご指摘されているとおり)

Added: July 17, 2015.


home