我的主要重点是扩展已经给出的样本更多部分通过附加功能(例如字幕)。虽然我成功地完成了字幕,但我在宏查询关于\expandafter
。感谢提供的样本,我弄清楚了工作样本应该是什么样的。
\documentclass{article}
\usepackage{keyval}
\makeatletter
\define@key{mosquito}{hideInToC}[false]{\def\mosquito@hideInToC{#1}}
\newcommand\sectionLvl[3][]{%
\setkeys{mosquito}{hideInToC={},#1}%
\ifcase#2\relax\expandafter\chapter\or
%section
\ifx\mosquito@hideInToC\@empty\expandafter\expandafter\expandafter\section%
\else\expandafter\expandafter\expandafter\section\expandafter\expandafter\expandafter*%
\fi
\else\expandafter\subsection%
\fi
{#3}
}%
\makeatother
\begin{document}
\tableofcontents
++++++++++ after ToC********
\sectionLvl{1}{title}
\sectionLvl[hideInToC]{1}{title1}
\sectionLvl{1}{title2}
\sectionLvl{2}{title2-1}
\end{document}
这种方法总体上可行,但之前的答案留下了一些问题。我的两个问题是:
- 根据
keyval
文档,我可以定义一个布尔值。但文档缺少如何检查该值的示例。\ifx\mosquito@hideInToC\@empty
正在运行 - 但这是检查布尔值的最佳方法吗? - 在给出的答案中宏查询我找到了类似的解决方案,
\ifnum#2=1 \expandafter\@firstoftwo\fi\section*{#1}%
看起来很棒。但是当我尝试在这里应用它时,\ifx\mosquito@hideInToC\@empty\expandafter\@firstoftwo\fi\section*%
评估总是失败。
所以总的来说我有一个可行的解决方案。但我很好奇它失败的原因以及所提出的方法在给定场景中失败的原因。
答案1
广告问题 1:
我认为处理布尔值的经典方法是使用布尔键,如以下软件包所提供的键值或者鍵盤或解释3。
如果你不喜欢使用这样的包/接口,你可以定义自己的机制,通过分隔参数来确定是否提供值“true/yes”或值“false/no”或其他值,并通过应用以下命令之一设置相应的-switch :\if⟨name of if-switch⟩
\⟨name of if-switch⟩true
\⟨name of if-switch⟩false
\documentclass{article}
\usepackage{keyval}
\makeatletter
%
%----------------------------------------------------------------------------------------------
%
% \truefalsefork{<value>}%
% {<tokens if <value>=true/yes>}%
% {<tokens if <value>=false/no>}%
% {<tokens> if <value> neither is true/yes nor is false/no}
%
\@ifdefinable\gobbletorelax{\long\def\gobbletorelax#1\relax{}}%
\newcommand\truefalsefork[1]{%
\lowercase{\@truefalsefork{#1}}%
}%
\newcommand\@truefalsefork[4]{%
\ifcat$\detokenize\expandafter{\gobbletorelax#1\relax}$%
\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\selecttruefalsecase
\relax#1\relax yes\relax false\relax no\relax{#2}%
\relax true\relax#1\relax false\relax no\relax{#2}%
\relax true\relax yes\relax #1\relax no\relax{#3}%
\relax true\relax yes\relax false\relax#1\relax{#3}%
\relax true\relax yes\relax false\relax no\relax{#4}\relax\relax\relax\relax
}{#4}%
}%
\@ifdefinable\selecttruefalsecase{%
\long\def\selecttruefalsecase#1\relax true\relax yes\relax false\relax no\relax#2#3\relax\relax\relax\relax{#2}%
}%
%
%----------------------------------------------------------------------------------------------
%
% Infrastructure for boolean-key hideInToc
%
\newcommand\hideInTocErrordefault[1]{%
\PackageError{sectionLvl}{%
Macro \string\sectionLvl:\MessageBreak
Invalid value '\detokenize{#1}'\MessageBreak
for key hideInToC\on@line.\MessageBreak
Value "true" is assumed%
}{%
Only values true/false/yes/no are allowed!\MessageBreak
(Lettercasing doesn't matter.)\MessageBreak
Value "true" is assumed.%
}%
\hideInToctrue
}%
\newif\ifhideInToc
\define@key{mosquito}{hideInToC}[true]{%
\truefalsefork{#1}{\hideInToctrue}{\hideInTocfalse}{\hideInTocErrordefault{#1}}%
}%
%
%----------------------------------------------------------------------------------------------
%
\newcommand\sectionLvl[3][]{%
\setkeys{mosquito}{hideInToC=false, #1}%
\ifcase#2\relax
\expandafter\chapter
\or
%section
\ifhideInToc\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\expandafter\section\expandafter*}{\expandafter\section}%
\else
\expandafter\subsection
\fi
{#3}%
}%
\makeatother
\begin{document}
\tableofcontents
%++++++++++ after ToC********
\sectionLvl{1}{title}
\sectionLvl[hideInToC=TrUe]{1}{title1}
% Check the error-message-mechanism by uncommenting the next line:
% \sectionLvl[hideInToC=WeiRd]{1}{title1}
\sectionLvl{1}{title2}
\sectionLvl{2}{title2-1}
\end{document}
请注意,这是一种穷人的布尔键实现。
上面提到的软件包提供了更为复杂的例程。
广告问题2:
查看您提供的代码时,我看到以下内容:
由于\ifx\mosquito@hideInToC\@empty..\else..\fi
-thingie 嵌套在-thingie\or
的最后一个 -branch中\ifcase..\or..\else..\fi
,既在 -thingie 的“真”分支中,也在\ifx\mosquito@hideInToC\@empty..\else..\fi
-thingie 的“假”分支中,因此您已采取措施确保\expandafter
存在一个 -chain,该 -chain 会触发\else
周围\ifcase..\or..\else..\fi
-thingie 后续分支的扩展,进而导致删除整个“周围”的“其他”分支,并且执行\fi
之前的\section
/ 。因此,由于这些-chain,来自-thingie 的“假”分支和来自 -thingie 的“真”分支的-chain 都可以作为其参数获取,因为没有其他来自周围-expression 的标记会妨碍。\section*
\expandafter
\section
\ifx\mosquito@hideInToC\@empty..\else..\fi
\section*
\ifx\mosquito@hideInToC\@empty..\else..\fi
{#3}
\ifcase..
如果您只是\ifx\mosquito@hideInToC\@empty..\else..\fi
用类似序列的东西替换 -thingie \ifx\mosquito@hideInToC\@empty\expandafter\@firstoftwo\fi\section*
,那么这样的-chain 就不存在了。因此,当收集/的参数时,来自周围-thingie 的\expandafter
标记仍然存在。因此,不是序列而是标记将成为/的参数,从而导致意外结果/意外行为。\else..\fi
\ifcase..\or..\else..\fi
\section
\section*
{#3}
\else
\section
\section*
您可以定义一个附加宏\@twooftwo
并执行以下操作:
\documentclass{article}
\usepackage{keyval}
\makeatletter
\define@key{mosquito}{hideInToC}[false]{\def\mosquito@hideInToC{#1}}
\newcommand\@twooftwo[2]{#1#2}
\newcommand\sectionLvl[3][]{%
\setkeys{mosquito}{hideInToC={},#1}%
\ifcase#2\relax
\expandafter\chapter
\or
%section
\ifx\mosquito@hideInToC\@empty\expandafter\@firstoftwo\else\expandafter\@twooftwo\fi
{\expandafter\section}{\expandafter*}%
\else
\expandafter\subsection
\fi
{#3}%
}%
\makeatother
\begin{document}
\tableofcontents
++++++++++ after ToC********
\sectionLvl{1}{title}
\sectionLvl[hideInToC]{1}{title1}
\sectionLvl{1}{title2}
\sectionLvl{2}{title2-1}
\end{document}
你也可以做“标准”的\if..\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
事情:
\documentclass{article}
\usepackage{keyval}
\makeatletter
\define@key{mosquito}{hideInToC}[false]{\def\mosquito@hideInToC{#1}}
\newcommand\sectionLvl[3][]{%
\setkeys{mosquito}{hideInToC={},#1}%
\ifcase#2\relax
\expandafter\chapter
\or
%section
\ifx\mosquito@hideInToC\@empty\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\expandafter\section}{\expandafter\section\expandafter*}%
\else
\expandafter\subsection
\fi
{#3}%
}%
\makeatother
\begin{document}
\tableofcontents
++++++++++ after ToC********
\sectionLvl{1}{title}
\sectionLvl[hideInToC]{1}{title1}
\sectionLvl{1}{title2}
\sectionLvl{2}{title2-1}
\end{document}
但这些只是一些例子,展示了如何在执行/之前让\expandafter
-tokens 排队删除\ifcase..\or..\else..\fi
-thingie ,从而让 TeX 收集/的参数。\else..\fi
\section
\section*
\section
\section*
我不推荐这样做,因为您没有基础设施来处理用户为密钥提供不适当值的情况hideInToC
。
答案2
可以通过包定义一个简单的布尔选项kvoptions
:
\documentclass{article}
\usepackage{keyval}
\usepackage{kvoptions}
\SetupKeyvalOptions{
family=mosquito,
prefix=mosquito@,
}
\DeclareBoolOption{hideInToC}
\makeatletter
\newcommand\sectionLvl[3][]{%
\begingroup
\setkeys{mosquito}{#1}%
\ifcase#2\relax % 0 = chapter
\def\tmp@mosquito{\chapter}%
\or % 1 = section
\ifmosquito@hideInToC
\def\tmp@mosquito{\section*}%
\else
\def\tmp@mosquito{\section}%
\fi
\else % subsection
\def\tmp@mosquito{\subsection}%
\fi
\expandafter\endgroup
\tmp@mosquito{#3}
}%
\makeatother
\begin{document}
\tableofcontents
\bigskip
\hrule
\sectionLvl{1}{Title 1 (shown)}
\sectionLvl[hideInToC]{1}{Title 2 (hideInToC)}
\sectionLvl{1}{Title 3 (shown)}
\sectionLvl{2}{SubTitle 4 (shown)}
\end{document}
评论:
- 链
\expandafter
被一个简单的临时宏定义所取代,\tmp@mosquito
以简化代码的可读性和维护性。我们在这里不需要可扩展性,因为 和 都不\chapter
是\section
完全可扩展的。 - 目前还不完全清楚,key
hideInToC
应该作用于全局还是局部。用法意味着局部范围。因此,我将 key 设置放在一个组中,这样它仅hideInToC
适用于当前组。\sectionLvl
广告 1:
无论有keyval
没有 package kvoptions
,布尔选项定义都由三部分组成:
布尔开关的定义:
\newif\ifmosquito@hideInToC
可选。默认值
false
与通常预期的一样。可以将其更改为true
:\mosquito@hideInToCtrue
选项定义:
\define@key{mosquito}{hideInToC}[true]{% \csname mosquito@hideInToC#1\endcsname }
唯一允许的值是
true
和false
。其他值将被忽略,因为\csname...\endcsname
行为就像\relax
命令序列未定义一样。可以添加错误检查:
\define@key{mosquito}{hideInToC}[true]{% \ifcsname mosquito@hideInToC#1\endcsname \csname mosquito@hideInToC#1\endcsname \else % Error message (\PackageError, \latex@error, ...) \fi }
使用形式:
hideInToC % true
hideInToC=true % true
hideInToC=false % false
可以定义一个相反的布尔选项:
\define@key{mosquito}{showInToC}[true]{%
\expandafter\ifx\csname if#1\endcsname\iftrue
\mosquito@hideInToCfalse
\else
% Caution: \iffalse inside \if constructs needs to be protected
\expandafter\ifx\csname if#1\expandafter\endcsname\csname iffalse\endcsname
\mosquito@hideInToCtrue
\else
% Error message
\fi
\fi
}
然后: showInToC % hideInToC=false showInToC=true % hideInToC=false showInToC=false % hideInToC=true
它是一个带有包装的单行程序kvoptions
:
\DeclareComplementaryOption{showInToC}{hideInToC}