
\uppercase
是否存在不受/影響\lowercase
且不可(重新)定義為 的標記\outer
?
(如果是這樣,我想將它用作參數分隔符,與\uppercase
/內可能使用的內容一起使用\lowercase
。)
答案1
Afaik 沒有這樣的令牌:
每個標記至少屬於以下兩類標記之一-活動字元標記同時屬於兩類:
- 控制序列。 (控製字標記、控制符號標記、活動字元標記。)所有控制序列均可(重新)定義為
\outer
。 - 顯式字元標記。每個顯式字元標記都受
\uppercase
/影響\lowercase
(只要相應地設定了\uccode
/\lccode
)。
在許多情況下,您可以透過使用不含分隔符號的參數並檢查是否為空來完全避免帶分隔符號的參數。
例如,為了從大括號平衡標記列表中提取第一個無限參數,我經常使用這樣的東西:
%% \romannumeral\UD@ExtractFirstArgLoop{<argument>\UD@SelDOm}%
%% yields <argument>'s 1st undlimited argument.
%% <argument> must not be blank, i.e., must neither be empty nor consist
%% only of explicit character tokens of catcode 10 and charcode 32.
%%
%% \UD@SelDOm must not be defined in terms of \outer !
%%.............................................................................
\@ifdefinable\UD@RemoveTillUD@SelDOm{%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
{\expandafter\z@\@secondoftwo{}#1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
(\UD@CheckWhetherNull
被定義為
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\expandafter\z@\@secondoftwo}%
{\expandafter\z@\@firstoftwo}%
}%
或作為
\newcommand\UD@CheckWhetherNull[1]{%
\romanumeral\ifcat$\detokenize{#1}$%
\expandafter\expandafter\expandafter\z@\expandafter\@firstoftwo\else
\expandafter\expandafter\expandafter\z@\expandafter\@secondoftwo\fi
}%
。 )
這樣你就得到:
\romannumeral\UD@ExtractFirstArgLoop{{A}{B}CDE\UD@SelDOm}%
→ A
。
為了減少刪除除第一個非分隔參數之外的所有內容所需的迭代量,我使用了由 分隔的參數\UD@SelDOm
。 (所需的迭代量為:「\UD@SelDOm
參數內未嵌套在大括號中的數量」+1)。
如果您不喜歡 -delimiter \UD@SelDOm
,因為它可能是根據 定義的\outer
,那麼您可以按如下方式進行操作,無需進行操作(代價是需要更多迭代直到獲得結果),所需的迭代量為:「未定界參數的數量在參數中,它們本身沒有嵌套在大括號中“+1:
% Syntax now is: \romannumeral\UD@ExtractFirstArgLoop{<argument>{}}%
\newcommand\UD@GrabFirst[2]{{#1}}%
\renewcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
{\expandafter\z@\@secondoftwo{}#1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@GrabFirst#1}}%
}%
例如,
\romannumeral\UD@ExtractFirstArgLoop{{A}{BC}DE{}}%
→A
在許多情況下,您可以執行類似的操作以完全避免分隔參數。
有時,人們會使用處理相關參數的巨集標記作為參數分隔符,例如\def\macro#1\macro{...}
。這是否可行/合理取決於問題是否\macro
可以嵌套在自己的參數中,從而錯誤地匹配分隔符,或者是否\let\macrob=\macro \outer\def\macro...
會發生類似的情況。
既然您已經在考慮如何使巨集參數安全的問題,我想指出除了\outer\def...
and \uppercase
/之外還有其他陷阱\lowercase
:
例如,基於分隔參數的巨集機制(應處理(幾乎)使用者給出的任意參數)是否應該在表格環境/對齊內工作的問題。
例如,假設有一個宏
\grabdelimited
,它處理分隔參數並且使用者使用它來收集和去標記字元B
和&
。\documentclass{article} \def\grabdelimited#1\delimiter{Arguments grabbed: \detokenize{#1}}% \begin{document} \grabdelimited B&\delimiter \makeatletter \begin{tabular}{|l|l|} %A&\relax\grabdelimited B&\delimiter\\ A&\relax\expandafter\@firstofone\expandafter{\grabdelimited B&\delimiter} \end{tabular} \end{document}
表格環境中的第一行/註解行會產生錯誤,第二行不會產生錯誤,因為這裡
&
屬於分隔參數的內容隱藏在 的花括號內\@firstofone
。另一個問題可能是將不平衡的
\if...
//作為巨集參數傳遞,這可能會錯誤地匹配處理\else
這些參數的巨集定義中出現的一些/ 。\fi
\if...
\else
與不平衡
\csname
/相同\endcsname
。
答案2
擴展評論:
如果您真的擔心您的參數結束標記可能會受到\uppercase
/ 的影響\lowercase
,您只需確保它不會出現在頂層,這樣就永遠不會被\uppercase
or看到\lowercase
。這可以透過確保它僅插入到擴展不能在其間停止的上下文中來實現,例如,透過使用擴展\romannumeral
上下文。下面設定一個宏,它使用該字元D
作為參數結束標記,但由於它進一步擴展,該標記永遠不會留在輸入流中,這樣它可能會受到\lowercase
or 的影響\uppercase
:
% first insert a \romannumeral such that the following is expanded as far as possible
\newcommand*\mymacro{\romannumeral\mymacro@a}
% only after \romannumeral has started input the delimiter
\newcommand\mymacro@a[1]{\mymacro@b #1D}
然後\mymacro@b
可以以可擴展的方式處理參數並用作D
分隔符號。你可以\romannumeral
用 結束擴充上下文\z@
。當然,您仍然可以擴展\mymacro
一次,然後\mymacro@a
在\expandafter
不啟動的情況下擴展\romannumeral
,這樣可能會受到(with )D
的影響,但至少現在這必須是出於惡意目的而創建的。\lowercase
\expandafter\expandafter\expandafter\lowercase\expandafter\expandafter\expandafter{\expandafter\expandafter\mymacro{}}
你永遠無法保護自己免受\outer
重新定義的影響,但重新定義其他人程式碼內部的人似乎\outer
不希望有工作程式碼,所以也許這不是你必須保護自己免受的情況。