p.33以降の2・3「複数個の引数」にて\trikaeというマクロをもとにして二番目の中括弧の引数を省略したときや引数に制御綴を与えたときの展開やその対処法などについて触れられています。そこに書かれている内容は理解した(と思う)のですが、「引数が2つ与えられたときはそれらを入れ替え、引数が1つのときは引数をそのまま表示する」といったマクロは作れないでしょうか? つまり、書籍中で出てきた引数を例にとると
\TRIKAE{ABC}{DEF} -> DEFABC
\TRIKAE{ABCDEF} -> ABCDEF
と出力されるようなマクロを定義することは可能でしょうか?(書籍中の\Trikaeなどと同様に\trikaeを用いてでもそうでなくても構いません。)
一応こちらに\trikaeの定義を書いておきますと\def\trikae#1#2{#2#1}です。(http://homepage3.nifty.com/xymtex/fujitas2/yatimata2/v200/yatimata2.pdf#14の1・3以降が同趣旨の文書です。)
これが出来るとかねてから作れたら便利だなと思っていたマクロが定義できそうなので質問させていただきました。
要領を得ない質問になってしまいましたが、何卒宜しくお願いします。
LaTeX的に素直なのは、\TRIKAEの引数の一方を
[~]で囲むオプション引数にすることかなあ。
\documentclass{jarticle}\relax
\makeatletter
\newcommand*{\TRIKAE}[2][\@nil]{%
\def\reserved@a{#1}%
\ifx \reserved@a \@nnil
#2%
\else
#2#1%
\fi
}
\makeatother
\begin{document}
引数が2個の場合:\TRIKAE[ABC]{DEF}
引数が1個の場合:\TRIKAE{ABCDEF}
\end{document}
片方の引数をオプション引数にすることは私も当初考えて実際にその方法では上手くいったのですが,作成したかったマクロにおいてオプショナルな要素がどちらかというと2番目に書くのが直感的・自然なものでしたので,オプション引数にするには違和感を覚えたので質問した次第です。
新たな疑問で恐縮なのですが,一番最初のレスにある\@ifnextchar\bgroupの仕組みが理解できていません。\@ifnextcharは私の中では\@ifnextchar〈判定したい文字〉{一致したときの処理}{一致しなかったときの処理}という書式という認識なのですが,この認識だと引数の個数関係なく\bgroupがマッチしてしまうのでマッチしなかった場合の#1という定義が採用されることがないように思いました。
\@ifnextcharの理解にどこか間違ってるところはありますでしょうか?(そもそもコードを読み違えている可能性もありますが)
> 新たな疑問で恐縮なのですが,一番最初のレスにある
> \@ifnextchar\bgroupの仕組みが理解できていません。
> \@ifnextcharは私の中では\@ifnextchar 判定したい文字
> {一致したときの処理}{一致しなかったときの処理}という書式という
> 認識なのですが,この認識だと引数の個数関係なく\bgroupが
> マッチしてしまうのでマッチしなかった場合の#1という定義が
> 採用されることがないように思いました。
最初に書いたマクロは、実際には「第2引数が省略されているかどうか」で
条件分岐しているのではなく、「第1引数の後ろに、引数を括る左ブレースが
あるかどうか」で条件分岐しているのです。
\@ifnextcharで第1引数の後ろの文字を調べてそれが\bgroup(これは
左ブレースの別名になっています)と一致するかどうかを見ているのです。
--------
> 片方の引数をオプション引数にすることは私も当初考えて実際に
> その方法では上手くいったのですが,作成したかったマクロにおいて
> オプショナルな要素がどちらかというと2番目に書くのが
> 直感的・自然なものでしたので,オプション引数にするには
> 違和感を覚えたので質問した次第です。
LaTeXで使うコマンドとしては、ブレースで囲む引数を省略可能であるかのように
扱うのは、やはり「行儀が悪い」と思います。
※ LaTeXの標準のコマンドでは、ブレースで囲むのは必須引数、ブラケットで
囲むのはオプション引数ということで統一が取られていますから。
第2引数をオプションにするというのは、オプションを[~]で囲む仕様に
した場合でも可能ですからその方がいいように思うのですが…
\documentclass{jarticle}\relax
\makeatletter
\newcommand*{\TRIKAE}[1]{%
\@ifnextchar[{\TRIKAE@{#1}}{#1}%
}
\def\TRIKAE@#1[#2]{%
#2#1%
}
\makeatother
\begin{document}
引数が1個の場合:\TRIKAE{ABCDEF}ghi
引数が2個の場合:\TRIKAE{ABC}[DEF]ghi
\end{document}
> \def\TRIKAE#1{%
>で始まっているので、\@ifnextcharが呼ばれた時点で第1引数(および
>それを囲むブレース)は既に食べられてしまっており、
>入力ストリームには残っていません。
というのはなんとなく理解できましたが,\defの書式上{}内に定義を書く前に#nの形で引数の個数を書かざるを得ないので,第一引数をオプションにする場合は制御綴名の直後に\@ifnextchar[を書く必要性と#1#2...の位置とが干渉するのではと感じました。
(今回のマクロでは偶然\newcommand{\trikae}[2][\relax]{#2#1}という書き方が出来ますが,作ろうとしたマクロの全部が全部このように上手くいくわけでもないので)
TeXのマクロは「徹頭徹尾置き換え」という認識でいないと
混乱します.
「関数」とか「サブルーチン」じゃないんです.
\def\hoge#1#2{...}
の#1と#2は引数ではなく
{...}の当該部分への「置換テキスト」なんです.
\hogeがあった場合,「後続の二つの要素」を取得して
それを{...}の中の当該部分に置き換えて
\hogeを「置き換え後の「...」」に変更するんです.
問題は「後続の二つの要素」という怪しげな言葉.
\hoge123
\hoge1{23}
\hoge{1},23
\hoge{1},{23}
\hoge{1}{23}
\hoge{1,2}34
\hoge1,2,3
\hoge(1,2)
いろいろなパターンがありますが,
細かいことをいわなければ
{}で囲まれていればそれを一塊,
そうでなければ一文字を一塊とみなします.
ですので
\def\torikae#1{\@ifnextchar[{\@torikae{#1}}{#1}}
\@torikae#1[#2]{#2#1}
は
\torikae{A}{B} => AB
\torikae{A}B => AB
\torikae{A}[B] => \@torikae{A}[B] => BA
です.
この手の展開は
\tracingmacros=1
\torikae{A}{B}
\tracingmacros=0
とするとlogに詳細がでてきますので
それをみると何が起きてるのか覗き込めます.
このオプション云々はlatex.ltxで
\@ifnextcharの定義をおいかけると
面白いかもしれません.
\@ifnextcharの定義の本質は
\futureletですが,
空白の読み飛ばしのための
「コントロールワード」と「コントロールシンボル」の
違いとか展開制御のテクニックがパズルみたいです.
仰っていらっしゃるように他のプログラミング言語の延長でTeXのマクロの処理法(と申すのが適切かはわかりませんが)を考えていたせいで理解できていなかったように思います。
回答を読んで自分で考えたところでは例に挙げていらっしゃいます
\def\torikae#1{\@ifnextchar[{\@torikae{#1}}{#1}}
\def\@torikae#1[#2]{#2#1} %\defがありませんでしたがこういうことですよね?
\torikae{A}[B]
がBAと出力されるのは
(\torikaeではなく\torikae#1が一まとまりとして)\torikae#1が\torikae{A}に置換されて{}内の定義が実行される
-> \trikae{A}の次の文字は[B]の[なので\@ifnextchar[の条件にマッチして\@torikae{#1}すなわち\@torikae{A}になる。(この時点で\def\torikae#1{\@ifnextchar[{\@torikae{#1}}{#1}}の処理が終了)
-> \torikae{A}の後ろにある[B]が上の処理で出てきた\@torikae{A}と合わさって\@torikae{A}[B]となり,\def\@torikae#1[#2]{#2#1}のよって最終的にBAが出力される
という過程を経ているということで宜しいでしょうか?
はいそうです.落ちてます
処理のタイミングとか諸々のそういう詳細は抜きにして
おおざっぱに考えます
\torikae{A}[B]
というのは
torikae
{
A
}
[
B
]
という風に分解されます.
#実際はもうちょっといろいろあります
torikae を処理(展開)しようとすると
定義より引数が一個必要です
{ があるので,対応する } までが引数です
そこで,torikae A がおきかわって
@ifnextchar [ { @torikae { A } } { A }
[
B
]
になります.
@ifnextcharの行は { A } の次の「もの」と
[ を比較して一致すれば
@torikae { A } に
そうでなければ A に置き換わります
このとき,
比較に使われた「次の行の [ 」は
残ります.今は一致したので
@torikae { A }
[ ←比較に使われたけど消費されないで残る
B
]
となります
これで結局 BA がでてきます
動きとしてはお書きになったご理解の通りですが
主観的には,もっと泥臭いというか
まめまめと置換と考える方が
TeXのマクロを考えるにはいいように感じます.