#1
をその値に基づいて別の値に変更し、変更後の値に元の値 (外部からマクロに渡された値) ではなく変更後の値が含まれるようにするにはどうすればよいか#1
と考えていました。
\def\myT#1%
{%
\ifnum#1=0
% #1=1
\fi
#1
}
\myT{0} % 1
\myT{1} % 1
\myT[2} % 2
編集 (回避策:以下のように の引数でネストされた を展開するのではなく、#1
のように の値を直接変更します):#1=1
\myT
1
\def\myT#1%
{%
\ifnum#1=0
\myT{1}%
\else
#1%
\fi
}
\myT{0} % 1
\myT{1} % 1
\myT{2} % 2
答え1
#1
適切な引数を使用せずに値を「割り当てる」ことはできません。
あなたが提示したタイプのタスクは、2 つのマクロを使用するとより便利に実行できます。\myT{1}
元の引数が のときに を呼び出すことはできますが0
、これはすでに実行されたチェックを再度実行することになるため、非効率的です。
\def\myT#1{%
\ifnum#1=0
\myTaux{1}%
\else
\myTaux{#1}%
\fi
}
\def\myTaux#1{-#1-\par}
\myT{0} % 1
\myT{1} % 1
\myT{2} % 2
\bye
必要な機能の動作を 2 つ以上のマクロに分割することは悪いことではありません。むしろ、一般的には良い方法です。
答え2
tex は関数型言語ではなく、マクロ処理言語であることを覚えておいてください。#1
関数への参照によって渡される変数を参照する識別子ではなく、提供されたトークンがインライン化される単なるプレースホルダーです。
したがって、何かを割り当てることが意味があるかどうかは、#1
何を割り当てるかによって異なります#1
。
与えられた
\def\myT#1%
{%
\ifnum#1=0
#1=1 %HERE
\fi
#1 %THERE
}
\myT{0} % 1
HEREとマークされた行は 0=1
代入ではなく、単に0=1とタイプセットするだけです。ただし、同じ定義を使用して次のように呼び出すと、
\newcount\zzz
\zzz=0
\myT{\zzz}
すると、HEREとマークされた行は\zzz=1
代入となり、THEREとマークされた行は代入となるため、必要な\zzz
タイプセットは行われず、次のように評価される。1
\the#1
\the\zzz
答え3
「引数の値を変更する」と説明されているものに最も近いプロセスは、問題のマクロが、問題の引数を変更して自分自身を再度呼び出すことです。
何らかの条件に応じてルーチンが最後に行うことは終了し、再度ルーチン自身を呼び出すというこのプログラミング手法は、末尾再帰と呼ばれます。
(La)TeX 用語では、(La)TeX マクロは、何らかの条件に応じて、マクロを展開した結果として配信された最後のトークンが別の呼び出しを形成する場合に末尾再帰的になります。
\if.. \else.. \fi
末尾再帰の終了条件を確認するための - スイッチが必要な場合は、該当するマクロが再度呼び出される前に、一致する\else
- および -\fi
ブランチを形成するトークンが入力バッファから処理/破棄されるようにしてください。そうでない場合、これらのブランチを形成するトークンは、再帰がすでに終了している時点で処理されるまでトークン ストリーム/入力バッファに残り、蓄積されるため、該当するマクロは末尾再帰ではありません。これにより、セマンティック ネストに不要な負担がかかり、入力バッファに不要なトークンが蓄積されます。
たとえば、次のようなことができます。
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\myT#1{%
\ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi
{\myT{1}}{#1}%
}%
\myT{0}% 1
\myT{1}% 1
\myT{2}% 2
%...
\bye
より複雑な変更が必要な場合は、必要な拡張手順をトリガーした後に引数を交換すると便利です。2 つの引数を処理するマクロを想定します。最初の引数の値が 0 の場合、その引数を値 1 の引数に置き換え、2 番目の引数を値が 1 増加する引数に置き換える必要があります。
% this example requires eTeX-extensions (\numexpr):
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\passfirsttosecond#1#2{#2{#1}}%
\def\myT#1#2{%
\ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi
{%
\expandafter\passfirsttosecond\expandafter{\number\numexpr#2+1\relax}{\myT{1}}%
}{%
Value of 1st arg: #1.\hfil\break
Value of 2nd arg: #2.%
}%
}%
\myT{0}{5}% Value of 1st arg: 1.\hfil\break Value of 2nd arg: 6.
\myT{1}{6}% Value of 1st arg: 1.\hfil\break Value of 2nd arg: 6.
\myT{2}{7}% Value of 1st arg: 2.\hfil\break Value of 2nd arg: 7.
%...
\bye
2 つ以上の引数を変更する必要がある場合は、の 2 番目の引数\passfirsttosecond
内で の呼び出しをネストすること\passfirsttosecond
ができます。これを行うと、変更が必要な最後から k 番目のマクロ引数の変更が、最初から k 番目の -\passfirsttosecond
ディレクティブを実行する直前に実行されるパターンが得られます。
9 つの引数を処理するマクロを想定します。最初の引数の値が 0 の場合、その引数を値 1 の引数に置き換え、次の各引数を値が 1 ずつ増加する引数に置き換える必要があります。
% this example requires eTeX-extensions (\numexpr):
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\passfirsttosecond#1#2{#2{#1}}%
\def\myT#1#2#3#4#5#6#7#8#9{%
\ifnum#1=0 \expandafter\firstoftwo\else\expandafter\secondoftwo\fi
{%
\expandafter\passfirsttosecond\expandafter{\number\numexpr#9+1\relax}{%
\expandafter\passfirsttosecond\expandafter{\number\numexpr#8+1\relax}{%
\expandafter\passfirsttosecond\expandafter{\number\numexpr#7+1\relax}{%
\expandafter\passfirsttosecond\expandafter{\number\numexpr#6+1\relax}{%
\expandafter\passfirsttosecond\expandafter{\number\numexpr#5+1\relax}{%
\expandafter\passfirsttosecond\expandafter{\number\numexpr#4+1\relax}{%
\expandafter\passfirsttosecond\expandafter{\number\numexpr#3+1\relax}{%
\expandafter\passfirsttosecond\expandafter{\number\numexpr#2+1\relax}{%
\expandafter\passfirsttosecond\expandafter{\number\numexpr#1+1\relax}{%
\myT
}%
}%
}%
}%
}%
}%
}%
}%
}%
}{%
Value of 1st arg: #1.\hfil\break
Value of 2nd arg: #2.\hfil\break
Value of 3rd arg: #3.\hfil\break
Value of 4th arg: #4.\hfil\break
Value of 5th arg: #5.\hfil\break
Value of 6th arg: #6.\hfil\break
Value of 7th arg: #7.\hfil\break
Value of 8th arg: #8.\hfil\break
Value of 9th arg: #9.%
}%
}%
\myT{0}{4}{9}{14}{19}{24}{29}{34}{39}%
% Value of 1st arg: 1.\hfil\break
% Value of 2nd arg: 5.\hfil\break
% Value of 3rd arg: 10.\hfil\break
% Value of 4th arg: 15.\hfil\break
% Value of 5th arg: 20.\hfil\break
% Value of 6th arg: 25.\hfil\break
% Value of 7th arg: 30.\hfil\break
% Value of 8th arg: 35.\hfil\break
% Value of 9th arg: 40.%
\myT{1}{5}{10}{15}{20}{25}{30}{35}{40}%
% Value of 1st arg: 1.\hfil\break
% Value of 2nd arg: 5.\hfil\break
% Value of 3rd arg: 10.\hfil\break
% Value of 4th arg: 15.\hfil\break
% Value of 5th arg: 20.\hfil\break
% Value of 6th arg: 25.\hfil\break
% Value of 7th arg: 30.\hfil\break
% Value of 8th arg: 35.\hfil\break
% Value of 9th arg: 40.%
\myT{2}{1}{2}{3}{4}{5}{6}{7}{8}%
% Value of 1st arg: 2.\hfil\break
% Value of 2nd arg: 1.\hfil\break
% Value of 3rd arg: 2.\hfil\break
% Value of 4th arg: 3.\hfil\break
% Value of 5th arg: 4.\hfil\break
% Value of 6th arg: 5.\hfil\break
% Value of 7th arg: 6.\hfil\break
% Value of 8th arg: 7.\hfil\break
% Value of 9th arg: 8.%
%...
\bye
上記の例からわかるように、(La)TeX プログラミング、特に (La)TeX の「拡張」の概念は、変数に値を割り当てることではなく、トークン ストリーム/入力ストリームに次々に配置されたもの/項目に対してトークンを取得できる、いわゆるトークンを回転させることに関するものです。
(La)TeX のマクロ展開の概念を理解するには、次の例えが役立つかもしれません。
(La)TeX が .tex 入力ファイル、つまり (La)TeX ソースコードの一部を処理するとき、最初の段階では、その .tex 入力ファイルからトークンをトークン ストリームに挿入するための一連の指示を取得します。(これらのトークンは、任意のカテゴリ コードの文字トークン、制御語トークン、および制御記号トークンです。) 後の段階で、これらのトークンが処理されます。拡張の段階では、トークンは削除されるか、他のトークンに置き換えられます。拡張の段階では、トークン ストリームに出現するトークンの順序も変更できます。
.tex入力ファイルを読む際に(La)TeXがトークンストリームにトークンを挿入する際に従う規則の詳細に興味がある場合は、議論を参照してください。「ソースコードのインデント」あなたにとって興味深いかもしれません。
(La)TeXにどのようなトークンがあるのか興味がある場合は、「マクロ」と「コマンド」の違いは何ですか?あなたにとって興味深いかもしれません。
マクロ引数が(La)TeXでどのように扱われるかに興味がある場合は、議論を参照してください。「TeX は区切られた引数をどのように検索しますか?」あなたにとって興味深いかもしれません。
拡大策略に興味がある方は、「csname マクロに追加するときに、expandafter の数を知るにはどうすればよいですか?」あなたにとって興味深いかもしれません。
siracusa がすでに指摘しているように、(La)TeX がトークン ストリームから引数を収集した場合、マクロ引数に別のトークン シーケンスを割り当てることはできません。
ただし、(La)TeX にその引数を調べさせて、最終的にどのトークンが渡されるかを「決定」させることはできます。これは直接実行することも、渡される引数が検査結果に依存する別のマクロを呼び出すことによって実行することもできます。
直接アプローチの例:
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\myT#1{%
\ifnum#1=0 %<- The space before the percent terminates \ifnum's second number, i.e., the number 0. It gets discarded silently.
\expandafter\firstoftwo%<-\expandafter removes the tokens that form the else-branch and the \fi
\else
\expandafter\secondoftwo%<-\expandafter removes the \fi
\fi
{1}{#1}%
}%
\myT{0} % 1
\myT{1} % 1
\myT[2} % 2
通常、\romannumeral
小文字の整数のローマ字表記を形成するトークンを取得するために使用されます。\romannumeral
数値の検索が必要な場合、(La)TeX は、数値が見つかるかエラー メッセージが表示されるまで、展開可能なトークンを展開し続けます。問題の数値が正でない場合、(La)TeX はトークンを出力せずに黙ってそれを飲み込みます。したがって、\romannumeral
最終的に (La)TeX が正でない数値を見つけることが保証されている限り、多くの展開作業と引数の検査作業をトリガーするために (または使用して) かまいません。
直接的なアプローチの別の例として、拡張のトリガーとして呼び出して、いずれの場合も の\romannumeral
結果が2 つの拡張ステップの後、またはで見つけた場所に2 回「ヒット」した後に配信されることが保証されます。\myT
\myT
\expandafter
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\def\myT#1{%
\romannunmeral0%<-While searching the number for \romannumneral, (La)TeX finds the digit-token 0 and keeps
% triggering expansion while searching in the token-stream for
% more digit-tokens or something that terminates the number.
\ifnum#1=0 %<- The space before the percent terminates \ifnum's second number, i.e., the number 0. It gets discarded silently.
\expandafter\firstoftwo%<-\expandafter removes the tokens that form the else-branch and the \fi
\else
\expandafter\secondoftwo%<-\expandafter removes the \fi
\fi
{ 1}{ #1}% <- The space that precedes `1`/`#1` will be right
% behind the "0" from "\romannumeral0".
% Therefore it stops (La)TeX's seeking for digits for
% \rommanumeral which means that after doing
% expansion-work/argument-examining-work (La)TeX
% does only find the non-positive number "0" and
% thus the result/effect of `\romannumeral` is:
% "Triggering doing a lot of expansion- and argument-
% examining-work while silently discarding the
% digit-token 0 which forms a non-positive number
% and the space behind it and not delivering
% anything in roman-notation at all."
}%
\myT{0} % 1
\myT{1} % 1
\myT[2} % 2
別のマクロを呼び出す例:
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\newcommand\myinnerT[1]{%
Here \firstoftwo{\LaTeX}{} can do to the argument, #1, whatever shall be done.%
}%
\def\myT#1{%
% Here the argument is examined for deciding how to call \myinnerT:
\ifnum#1=0 %
\expandafter\firstoftwo
\else
\expandafter\secondoftwo
\fi
{\myinnerT{1}}{\myinnerT{#1}}%
}%
\myT{0} % 1
\myT{1} % 1
\myT[2} % 2
上記の例では、展開のどの段階で引数が数値を生成するかをチェックする処理が実装されていないことに注意してください。(厳密に言えば、引数が任意のトークンシーケンスで構成される場合、そのようなチェックは不可能です。この場合、引数を形成するトークン自体が、何らかのアルゴリズムの(La)TeX実装を形成する可能性があるため、そのようなチェックは(他のものよりも)次の式を解く必要があります。停止問題。