我想知道如何#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
除了使用適當的參數之外,您不能“分配”值。
您提出的任務類型使用兩個巨集可以更方便地完成;雖然您可以\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
請記住,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
如果您需要進行更複雜的更改,在觸發所需的擴展步驟後交換參數可能會很好。假設一個處理兩個參數的宏,如果第一個參數的值為 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{%
\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
如果需要更改兩個以上的參數,您可以在第二個參數\passfirsttosecond
中嵌套呼叫。\passfirsttosecond
如果您這樣做,您會得到一種模式,其中需要更改的最後一個但第 k 個巨集參數的變更將在執行第一個但第 k 個\passfirsttosecond
指令之前執行:
假設一個巨集處理九個參數,並且在第一個參數值為 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-input-file,即 (La)TeX 原始碼的某些片段時,它在第一階段將 .tex-input-file 作為一組指令,用於將標記插入到令牌流。 (這些標記可以是任何類別代碼、控製字標記和控制符號標記的字元標記。)在稍後的階段,將處理這些標記。在擴張階段,代幣將被刪除/被其他代幣取代。在擴展階段,令牌在令牌流中出現的順序也可以改變。
如果您對 (La)TeX 在讀取 .tex-input-file 時將令牌插入令牌流所遵循的規則的詳細資訊感興趣,請參閱討論“原始碼縮排”您可能感興趣。
如果您對 (La)TeX 中有哪些類型的標記感興趣,請參閱討論“‘宏’和‘命令’有什麼區別?”您可能感興趣。
如果您對 (La)TeX 中如何處理巨集參數感興趣,請參閱討論“TeX 如何找到分隔參數?”您可能感興趣。
如果您對擴展技巧感興趣,請討論“附加到 csname 巨集時,如何知道擴充後的數量?”您可能感興趣。
正如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 找到一個非正數,您就可以(ab?)用於觸發大量擴展工作和論證檢查工作。
直接方法的另一個例子,呼叫\romannumeral
作為擴展的觸發器,以便確保在任何情況下都在兩個擴展步驟之後/在“擊中”您找到的位置兩次之後\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 實現,因此這樣的檢查將(在其他事情)需要解決停止問題.)