如何更改參數的值(例如#1),以便對它的任何引用(參數#1)都會擴展到新值?

如何更改參數的值(例如#1),以便對它的任何引用(參數#1)都會擴展到新值?

我想知道如何#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 實現,因此這樣的檢查將(在其他事情)需要解決停止問題.)

相關內容