我感兴趣的是查找特定部门单位的“父”计数器。例如,在以下文档结构中
1. A section
1.1 A subsection
1.1.1 A subsubsection
1.1.2 A subsubsection
1.1.2.1 A paragraph
1.2 A subsection
1.2.1 A subsubsection
1.2.1.1 A paragraph
1.2.1.2 A paragraph
1.2.2 A subsubsection
1.2.1 小节的(直接)“父级”是 1.2(小节)。1.1.2.1(段落)的(直接)“父级”是 1.1.2(小节)。
解决方案应该是
\parentlabel
可以在某个部门单位内调用的形式;- 能够处理没有父级的部门单元。例如,如果部门单元是
\chapter
,\thechapter
定义为\arabic{chapter}
,\parentlabel
则应为空{}
(例如);并且 - 足够灵活,以至于计数器显示的重新定义不起作用。也就是说,如果选择使用
-
而不是.
,解决方案应该仍然有效。所以我不认为字符串解析(使用,比如,xstring
) 将会很有用。
这是一次尝试和一个最小的游乐场:
\documentclass{article}
\def\parentlabel{\relax}% Placeholder for parent counter/level/label
\makeatletter
\let\oldsection\section
\renewcommand{\section}{\edef\parentlabel{\@currentlabel}\oldsection}%
\let\oldsubsection\subsection
\renewcommand{\subsection}{\edef\parentlabel{\@currentlabel}\oldsubsection}%
\let\oldsubsubsection\subsubsection
\renewcommand{\subsubsection}{\edef\parentlabel{\@currentlabel}\oldsubsubsection}%
\let\oldparagraph\paragraph
\renewcommand{\paragraph}{\edef\parentlabel{\@currentlabel}\oldparagraph}%
\newcommand{\showparent}{Current: \@currentlabel; Parent: \parentlabel}
\makeatother
\setcounter{secnumdepth}{4}
\begin{document}
\section{A section} \showparent
\subsection{A subsection} \showparent
\subsubsection{A subsubsection} \showparent
\subsubsection{A subsubsection} \showparent
\paragraph{A paragraph} \showparent
\subsection{A subsection} \showparent
\subsubsection{A subsubsection} \showparent
\paragraph{A paragraph} \showparent
\paragraph{A paragraph} \showparent
\subsubsection{A subsubsection} \showparent
\end{document}
该方法依赖于\@currentlabel
通过分段命令更新之前的捕获。
显然,这种方法在深入细分各个部分时效果很好。然而,回到更高的级别时,\parentlabel
无法准确地表示“父”标签。
答案1
\documentclass{article}
\def\determinecurrentlevel{%
\ifnum\value{section}=0
0
\else
\ifnum\value{subsection}=0
1
\else
\ifnum\value{subsubsection}=0
2
\else
\ifnum\value{paragraph}=0
3
\else
4
\fi
\fi
\fi
\fi
}
\def\showparent{%
\ifcase\determinecurrentlevel
\or
\ifdefined\chapter\thechapter\fi
\or
\thesection
\or
\thesubsection
\or
\thesubsubsection
\or
\theparagraph
\fi}
\setcounter{secnumdepth}{4}
\begin{document}
\section{A section} \showparent
\subsection{A subsection} \showparent
\subsubsection{A subsubsection} \showparent
\subsubsection{A subsubsection} \showparent
\paragraph{A paragraph} \showparent
\subsection{A subsection} \showparent
\subsubsection{A subsubsection} \showparent
\paragraph{A paragraph} \showparent
\paragraph{A paragraph} \showparent
\subsubsection{A subsubsection} \showparent
\end{document}
可以使用以下方法减少较长的条件连接\numexpr
:
\def\determinecurrentlevel{%
\numexpr
\ifnum\value{section}>0 1+\fi
\ifnum\value{subsection}>0 1+\fi
\ifnum\value{subsubsection}>0 1+\fi
\ifnum\value{paragraph}>0 1+\fi
0\relax}
答案2
这是一个基于使用expl3
数据类型的相当通用的解决方案prop
。完整代码比其他解决方案略长。但是,它提供了一个完全通用的解决方案,可以开箱即用地与任意计数器组合配合使用。
主要思想是,在 LaTeX2e 中已经对此提供了很大一部分支持,因为计数器通常由其父计数器重置。因此,作为起点,我们假设此重置定义了父结构并添加代码以\@addtoreset
存储父计数器。如果这还不够,我们提供\setparent
手动指定父计数器。
我们需要的第二个组件是提供一个并行组件,我们将它添加到其中\@currentcounter
。最后,我们提供一个计数器名称,用于排版(只需将其中一行放入单独的宏中即可)。\@currentlabel
\refstepcounter
\builtcurrentlabel
\refstepcounter
\showparent
然后只需排版当前标签和从父计数器生成的标签(如果有):
\makeatletter
\def\@addtoreset#1#2{%
\expandafter\@cons\csname cl@#2\endcsname {{#1}}%
% NEW: store parent info
\setparent {#1}{#2}%
}
\def\refstepcounter#1{\stepcounter{#1}%
\protected@edef\@currentlabel
{\csname p@#1\endcsname\csname the#1\endcsname}%
% NEW: save current counter
\protected@edef\@currentcounter{#1}%
}
% NEW: taken from refstepcounter
\def\builtcurrentlabel #1{%
\csname p@#1\endcsname
\csname the#1\endcsname
}
\def\showparent{%
Current: \@currentlabel; Parent: \typesetparentlabel{\@currentcounter}%
}
\makeatother
现在剩下要做的就是实际存储和检索父计数器映射。顺便说一下,所有这些都必须先发生,\documentclass
以便计数器仍需定义,否则\@addtoreset
将不起作用。所以我们必须使用\RequirePackage
来加载 expl3。
我们将父计数器名称存储在属性列表 ( \g_pcnt_parent_counter_prop
) 中,其中当前计数器名称作为键,父计数器作为值。这里唯一的额外复杂性是,将一个没有父计数器的新计数器添加到计数器中@ckpt
(用于包含机制),我们不想将其视为父计数器:
\RequirePackage{expl3}
\ExplSyntaxOn
\prop_new:N \g_pcnt_parent_counter_prop
\tl_new:N \l_pcnt_result_tl
\cs_new:Npn \pcnt_store_parent_counter:nn #1#2 {
\str_if_eq:nnF{@ckpt}{#2}
{ \prop_gput:Nnn \g_pcnt_parent_counter_prop {#1}{#2} }
}
检索同样简单:如果我们没有找到值,我们将返回一个空的标记列表
\cs_new:Npn \pcnt_get_parent_counter:nN #1#2 {
\prop_get:NnN \g_pcnt_parent_counter_prop {#1} #2
\quark_if_no_value:NT #2
{ \tl_clear:N #2 }
}
\cs_generate_variant:Nn \pcnt_get_parent_counter:nN {o} % needed below
最后的界面:排版父标签意味着检索父计数器名称,如果不为空则运行它\builtcurrentlabel
:
\cs_new_eq:NN \setparent \pcnt_store_parent_counter:nn
\cs_new:Npn \typesetparentlabel #1 {
\pcnt_get_parent_counter:oN {#1} \l_pcnt_result_tl
\tl_if_empty:NF \l_pcnt_result_tl
{ \builtcurrentlabel{\l_pcnt_result_tl} }
}
如果我们愿意,我们还可以定义以下数据结构:
%% for tracing %%
\cs_new:Npn \showparentcounters {\prop_show:N \g_pcnt_parent_counter_prop }
\cs_new:Npn \showparentcounter #1 {
\pcnt_get_parent_counter:nN {#1} \l_pcnt_result_tl
\tl_show:N \l_pcnt_result_tl
}
在问题的示例文档中将\showparentcounters
产生
The property list \g_pcnt_parent_counter_prop contains the pairs (without
outer braces):
> {subsection} => {section}
> {subsubsection} => {subsection}
> {paragraph} => {subsubsection}
> {subparagraph} => {paragraph}.
答案3
一种灵活的方法是在每个分段命令中定义钩子来指示父计数器,而不必担心任何格式,这些格式将在相关\the
命令中可用,例如\thesection
。建议:
\@namedef{part}{}
\@namedef{parent@chapter}{\the\c@part}
\@namedef{parent@section}{\the\c@chapter}
\@namedef{parent@subsection}{\the\c@section}
\@namedef{parent@subsubsection}{\the\c@subsection}
\@namedef{parent@paragraph}{\the\c@subsubsection}
宏中存储的内容parent@section
取决于您的需求。c@section
如果您想在此之后进行任何详细操作,则可以仅存储部分。这只是创建指向父级的指针的一种方式。
我们还定义了两个方便测试的宏:
\def\sectionlist{part,chapter,section,subsection,
subsubsection,paragraph}
\def\checkall{\@for\next:=\sectionlist\do{
\next\ has parent = \csname parent@\next\endcsname\\
}
完整极简图如下所示:
\documentclass{book}
\makeatletter
\makeatletter
\setcounter{secnumdepth}{6}
\def\sectionlist{part,chapter,section,subsection,
subsubsection,paragraph}
\@namedef{part}{}
\@namedef{parent@chapter}{\the\c@part}
\@namedef{parent@section}{\the\c@chapter}
\@namedef{parent@subsection}{\the\c@section}
\@namedef{parent@subsubsection}{\the\c@subsection}
\@namedef{parent@paragraph}{\the\c@subsubsection}
\def\checkall{\@for\next:=\sectionlist\do{
\next\ has parent = \csname parent@\next\endcsname\\
}
}
\makeatother
\begin{document}
\part{One}
\checkall
\chapter{One}
\checkall
\chapter{Two}
\checkall
\section{One}
\checkall
\subsection{One}
\checkall
\subsubsection{One}
\checkall
\paragraph{Test paragraph}
\checkall
\end{document}