Viết ngay @ifnextchar
Trong khi viết bài kiểm tra cho l3build, tôi đã gặp sự cố khi xuất kết quả của một lệnh được sử dụng @ifnextchar. Cụ thể, \TYPE{\command}đã xảy ra lỗi biên dịch. Tôi đã giảm nó xuống MWE sau:
\documentclass{article}
\begin{document}
\makeatletter
\DeclareRobustCommand\lookahead{\@ifnextchar{z}{hello}{goodbye}}
\immediate\write128{\lookahead z}
\makeatother
\end{document}
Trên máy tính của tôi (pdflatex, TeX Live 2020 / Debian) mã trước đó cung cấp cho các lỗi: Argument of \reserved@a has an extra }. Ở đây tôi chưa hiểu sâu về vấn đề này, vì vậy sau khi tìm kiếm, tôi quyết định tốt nhất là nên nhờ giúp đỡ. Bạn có biết tại sao nó lại mắc lỗi này và người ta có thể làm gì để giải quyết nó không? Lưu ý rằng không nên thay đổi phương thức đầu ra, vì nó về cơ bản là phương thức được sử dụng bởi l3build .
Cảm ơn nhiều!
Trả lời
Tôi có thể cung cấp một cơ chế có thể mở rộng \UD@CheckWhetherLeadingTokensbằng cách bạn có thể kiểm tra LaTeX bằng các macro xử lý các đối số được phân tách xem các mã thông báo đứng đầu của đối số macro có tạo thành một bộ mã cụ thể hay không.
\UD@CheckWhetherLeadingTokenskhác với \@ifnextchar/ \kernel@ifnextcharở một số khía cạnh:
\UD@CheckWhetherLeadingTokenscó thể mở rộng.\UD@CheckWhetherLeadingTokenskhông "nhìn về phía trước" ở mã thông báo tiếp theo trong luồng mã thông báo. Thay vào đó, nó "nhìn" vào các mã thông báo đầu tiên của một đối số vĩ mô.
(Cả với \@ifnextchar/ \kernel@ifnextcharvà với \UD@CheckWhetherLeadingTokensbạn có thể cần phải chú ý khi \uppercase/ \lowercase/ \MakeUppercase/ \MakeLowercasevà những thứ tương tự đóng vai trò rô-to.)
\documentclass{article}
\makeatletter
%==========[code for checking leading token-sequences in arguments]============
%% 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>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
{\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Exchange two arguments. (From each argument an outermost level of
%% surrounding braces will be removed if present.)
%%-----------------------------------------------------------------------------
\newcommand\UD@Exchange[2]{#2#1}%
%%-----------------------------------------------------------------------------
%% Check whether argument's leading tokens form a specific
%% token-sequence that does not contain explicit character tokens of
%% category code 1 or 2:
%%.............................................................................
%% \UD@CheckWhetherLeadingTokens{<argument which is to be checked>}%
%% {<a <token sequence> without explicit
%% character tokens of category code
%% 1 or 2>}%
%% {a <single non-space token> that does
%% _not_ occur in <token sequence> >}%
%% {<internal token-check-macro>}%
%% {<tokens to be delivered in case
%% <argument which is to be checked> has
%% <token sequence> as leading tokens>}%
%% {<tokens to be delivered in case
%% <argument which is to be checked>
%% does not have <token sequence> as
%% leading tokens>}%
\newcommand\UD@CheckWhetherLeadingTokens[4]{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\UD@Exchange{ }\expandafter\@secondoftwo}%
{\expandafter\@secondoftwo\string{\expandafter
\UD@@CheckWhetherLeadingTokens#4#3#1#2}{}}%
}%
\newcommand\UD@@CheckWhetherLeadingTokens[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
{\UD@Exchange{\@firstoftwo}}{\UD@Exchange{\@secondoftwo}}%
{\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% \UD@internaltokencheckdefiner{<internal token-check-macro>}%
%% {<token sequence>}%
%% Defines <internal token-check-macro> to snap everything
%% until reaching <token sequence>-sequence and spit that out
%% nested in braces.
%%-----------------------------------------------------------------------------
\newcommand\UD@internaltokencheckdefiner[2]{%
\@ifdefinable#1{\long\def#1##1#2{{##1}}}%
}%
%=======[end of code for checking leading token-sequences in arguments]=========
\UD@internaltokencheckdefiner{\zcheck}{z}%
\newcommand\lookahead[1]{%
\UD@CheckWhetherLeadingTokens{#1}{z}{.}{\zcheck}{hello}{goodbye} #1%
}%
\makeatother
\begin{document}
\immediate\write128{ble ble \lookahead{y bla} blu blu}
\immediate\write128{ble ble \lookahead{{z} bla} blu blu}
\immediate\write128{ble ble \lookahead{z bla} blu blu}
\end{document}
Với ví dụ trên, tôi nhận được điều này trên thiết bị đầu cuối:
ble ble goodbye y bla blu blu
ble ble goodbye {z} bla blu blu
ble ble hello z bla blu blu
Một cách tiếp cận khác có thể là \lookaheadthực hiện một cú đúp-hack để loại bỏ dấu ngoặc nhọn trước khi gọi một macro khác \lookaheadbđể thực sự thực hiện lookahead thông qua \@ifnextcharvà \@ifnextcharthực hiện một cú đúp-hack khác để thêm dấu ngoặc nhọn và gọi \immediate\writeđối số:
\documentclass{article}
\makeatletter
\newcommand\lookahead{%
\expandafter\expandafter\expandafter\lookaheadb\expandafter\@gobble\string
}%
\newcommand\lookaheadb{%
\@ifnextchar{z}%
{\immediate\write128\expandafter\expandafter\expandafter{\expandafter\@gobble\string}hello }%
{\immediate\write128\expandafter\expandafter\expandafter{\expandafter\@gobble\string}goodbye }%
}%
\makeatother
\begin{document}
\lookahead{y bla bla bla}
\lookahead{{z} bla bla bla}
\lookahead{z bla bla bla}
\end{document}
Với ví dụ trên, tôi nhận được điều này trên thiết bị đầu cuối:
goodbye y bla bla bla
goodbye {z} bla bla bla
hello z bla bla bla
Với cách tiếp cận này \lookaheadkhông thể được lồng vào bên trong \writelệnh mà đang gọi \writelệnh -command.