我的 MWE 使用 LuaLaTeX:
\documentclass[oneside,DIV=12]{scrbook}
\usepackage{scrhack}
\usepackage[automark]{scrlayer-scrpage}
\usepackage[english]{babel}
\usepackage[babel]{microtype}
\usepackage{mathtools, amssymb}
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} % Math fonts
\setmathfont{Latin Modern Math}
\usepackage{setspace}\setdisplayskipstretch{}
\usepackage{enumitem}
\usepackage{xparse}
\usepackage{environ}
\usepackage{keyval}
\usepackage{lipsum}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% \adjintertext custom spacing \intertext macro (https://tex.stackexchange.com/a/280847/228055)
\MHInternalSyntaxOn
\newcommand{\adjintertext}[3]% #1=above skip, #2=below skip, #3=text
{\ifvmode\else\\\@empty\fi
\noalign{%
%\penalty\postdisplaypenalty\vskip\belowdisplayskip
\vskip-\lineskiplimit % CCS
\vskip\normallineskiplimit % CCS
\vskip#1
\vbox{\normalbaselines
\ifdim
\ifdim\@totalleftmargin=\z@
\linewidth
\else
-\maxdimen
\fi
=\columnwidth
\else \parshape\@ne \@totalleftmargin \linewidth
\fi
\noindent#3\par}%
%\penalty\predisplaypenalty\vskip\abovedisplayskip%
\vskip-\lineskiplimit % CCS
\vskip\normallineskiplimit % CCS
\vskip#2
}}%
\MHInternalSyntaxOff
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}
\onehalfspacing\KOMAoptions{DIV=current}
\lipsum[1][1-6]
\begin{flalign*}
& \text{For any numbers \(a\)}
&& \tag{P'10} \\
%%%%% the part related to the question %%%%%
\adjintertext{0pt}{12pt}{\centering%
$\begin{alignedat}{2}
& \text{(i) } & a &= b, \\
& \text{(ii) } & a &< b, \\
& \text{(iii) } & b &< a.
\end{alignedat}$%
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) and \(b < c\), then \(a < c\).}
&& \tag{P'11} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\), then \(a + c < b + c\).}
&& \tag{P'12} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) abd \(0 < c\), then \(ac < bc\).}
&& \tag{P'13}
\end{flalign*}
\lipsum[2][1-8]
\end{document}
我想创建一个新的环境myalignedat
来排版居中和对齐的方程式,就像我的 MWE 一样,这样就不用再输入
\begin{<display math mode environment>}
\adjintertext{0pt}{12pt}{\centering%
\(\begin{alignedat}{2}
& \text{(i) } & a &= b, \\
& \text{(ii) } & a &< b, \\
& \text{(iii) } & b &< a.
\end{alignedat}\)%
}
\end{<display math mode environment>}
我会输入以下内容:
\begin{<display math mode environment>}
\begin{myalignedat}{2}[before skip=0pt, after skip=12pt]
& \text{(i) } & a &= b, \\
& \text{(ii) } & a &< b, \\
& \text{(iii) } & b &< a.
\end{myalignedat}
\end{<display math mode environment>}
类似于tcolorbox
为其环境做这件事。
我的问题是:我该如何定义这样的环境?
我尝试过使用\newenviron
或environ
和xparse
包,但由于我对此事了解甚少,因此无济于事。谢谢。
答案1
原始答案
为了使其工作,我们必须以某种方式将\noalign
置于 TeX 可以找到它的位置(在定义内部\begin{myalignedat}
这样做似乎太晚了)。因此,下面使用env/myalignedat/before
和env/myalignedat/after
钩子以及一些括号技巧(\ifnum0=`{
和\ifnum0=`}
)将 置于\noalign
我们需要的位置。此外,我们需要\adjintertext
在我们的环境中重新实现代码。
一旦我们解决了这个问题,剩下的就很简单了,我们只需收集一个可选参数和一个强制参数,并使用任何key=val
符合我们需求的实现(以下使用expkv
键expkv-def
定义前端1,但其他包也可以使用)。另一个小调整(虽然这不是必需的)是以下内容不使用\begin{alignedat}...\end{alignedat}
but \alignedat...\endalignedat
。
完整代码:
\documentclass[oneside,DIV=12]{scrbook}
\usepackage{scrhack}
\usepackage[automark]{scrlayer-scrpage}
\usepackage[english]{babel}
\usepackage[babel]{microtype}
\usepackage{mathtools, amssymb}
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} % Math fonts
\setmathfont{Latin Modern Math}
\usepackage{setspace}\setdisplayskipstretch{}
\usepackage{enumitem}
\usepackage{xparse}
\usepackage{environ}
\usepackage{lipsum}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% \adjintertext custom spacing \intertext macro (https://tex.stackexchange.com/a/280847/228055)
\MHInternalSyntaxOn
\newcommand{\adjintertext}[3]% #1=above skip, #2=below skip, #3=text
{\ifvmode\else\\\@empty\fi
\noalign{%
%\penalty\postdisplaypenalty\vskip\belowdisplayskip
\vskip-\lineskiplimit % CCS
\vskip\normallineskiplimit % CCS
\vskip#1
\vbox{\normalbaselines
\ifdim
\ifdim\@totalleftmargin=\z@
\linewidth
\else
-\maxdimen
\fi
=\columnwidth
\else \parshape\@ne \@totalleftmargin \linewidth
\fi
\noindent#3\par}%
%\penalty\predisplaypenalty\vskip\abovedisplayskip%
\vskip-\lineskiplimit % CCS
\vskip\normallineskiplimit % CCS
\vskip#2
}}%
\MHInternalSyntaxOff
\makeatletter
\usepackage{expkv-def}
\ekvdefinekeys{myalignedat}
{
skip before skip = \myalignedat@before
,skip after skip = \myalignedat@after
}
\AddToHook{env/myalignedat/before}
{\ifvmode\else\\\@empty\fi\noalign{\ifnum0=`}\fi}
\AddToHook{env/myalignedat/after}
{\ifnum0=`{\fi}}
\MHInternalSyntaxOn
\newenvironment{myalignedat}[2][]
{%
\ekvset{myalignedat}{#1}%
\vskip\myalignedat@before
\vbox\bgroup\normalbaselines
\ifdim
\ifdim\@totalleftmargin=\z@
\linewidth
\else
-\maxdimen
\fi
=\columnwidth
\else
\parshap\@ne\@totalleftmargin \linewidth
\fi
\noindent
\centering\(\alignedat{#2}%
}
{%
\endalignedat\)%
\par\egroup
\vskip-\lineskiplimit
\vskip\normallineskiplimit
\vskip\myalignedat@after
}
\MHInternalSyntaxOff
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}
\onehalfspacing\KOMAoptions{DIV=current}
\lipsum[1][1-6]
\begin{flalign*}
& \text{For any numbers \(a\)}
&& \tag{P'10} \\
%%%%% the part related to the question %%%%%
\begin{myalignedat}[before skip=0pt, after skip=12pt]{2}
& \text{(i) } & a &= b, \\
& \text{(ii) } & a &< b, \\
& \text{(iii) } & b &< a.
\end{myalignedat}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) and \(b < c\), then \(a < c\).}
&& \tag{P'11} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\), then \(a + c < b + c\).}
&& \tag{P'12} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) abd \(0 < c\), then \(ac < bc\).}
&& \tag{P'13}
\end{flalign*}
\lipsum[2][1-8]
\end{document}
输出:
1免责声明:我是软件包的作者expkv
和朋友
改进版本需要破解\begin
上述代码版本无法检测 a\\
之前是否使用过,因此导致垂直间距不一致。以下代码通过修补 LaTeX\begin
以添加另一个钩子来修复此问题。修补程序应该没问题,并且不会影响 的任何其他使用\begin
。
\documentclass[oneside,DIV=12]{scrbook}
\makeatletter
% redefine `\begin` to have another hook. This hook is no generic hook, so we
% have to use `\NewHook{env/#1/evenbeforebefore}` before we can use `\AddToHook`
% on it (for each environment that needs it)
\edef\begin#1%
{%
\unexpanded
{%
\ifx\protect\relax
\else
\expandafter\@gobbletwo
\fi
}%
\noexpand\UseHook{env/#1/evenbeforebefore}%
\noexpand\protect\expandafter\noexpand\csname begin \endcsname{#1}%
}
\makeatother
\usepackage{scrhack}
\usepackage[automark]{scrlayer-scrpage}
\usepackage[english]{babel}
\usepackage[babel]{microtype}
\usepackage{mathtools, amssymb}
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} % Math fonts
\setmathfont{Latin Modern Math}
\usepackage{setspace}\setdisplayskipstretch{}
\usepackage{lipsum}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatletter
\usepackage{expkv-def}
\ekvdefinekeys{myalignedat}
{
skip before skip = \myalignedat@before
,skip after skip = \myalignedat@after
}
\NewHook{env/myalignedat/evenbeforebefore}
\AddToHook{env/myalignedat/evenbeforebefore}
{\ifvmode\else\\\@empty\fi\noalign{\ifnum0=`}\fi}
\AddToHook{env/myalignedat/after}
{\ifnum0=`{\fi}}
\MHInternalSyntaxOn
\newenvironment{myalignedat}[2][]
{%
\ekvset{myalignedat}{#1}%
\vskip\myalignedat@before
\vbox\bgroup\normalbaselines
\ifdim
\ifdim\@totalleftmargin=\z@
\linewidth
\else
-\maxdimen
\fi
=\columnwidth
\else
\parshap\@ne\@totalleftmargin \linewidth
\fi
\noindent
\centering\(\alignedat{#2}%
}
{%
\endalignedat\)%
\par\egroup
\vskip-\lineskiplimit
\vskip\normallineskiplimit
\vskip\myalignedat@after
}
\MHInternalSyntaxOff
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}
\onehalfspacing\KOMAoptions{DIV=current}
\lipsum[1][1-6]
\begin{flalign*}
& \text{For any numbers \(a\) with much text following it}
&& \tag{P'10} \\
%%%%% the part related to the question %%%%%
\begin{myalignedat}[before skip=0pt, after skip=0pt]{2}
& \text{(i) } & a &= b, \\
& \text{(ii) } & a &< b, \\
& \text{(iii) } & b &< a.
\end{myalignedat}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) and \(b < c\), then \(a < c\).}
&& \tag{P'11} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\), then \(a + c < b + c\).}
&& \tag{P'12} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) abd \(0 < c\), then \(ac < bc\).}
&& \tag{P'13}
\end{flalign*}
\lipsum[2][1-8]
\end{document}
伪环境版本
此版本使用完全可扩展的宏来启动伪环境。它不需要对标准 LaTeX 宏进行任何黑客攻击,但它仍然使用括号黑客攻击。它\noalign
直接扩展到,然后可以使用不可扩展的参数抓取和 key=value 解析。
环境由 开始\beginmyalignedat[<key=value>]{<alignedat-arg>}
,并由 结束\stopmyalignedat
。
完整代码:
\documentclass[oneside,DIV=12]{scrbook}
\usepackage{scrhack}
\usepackage[automark]{scrlayer-scrpage}
\usepackage[english]{babel}
\usepackage[babel]{microtype}
\usepackage{mathtools, amssymb}
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} % Math fonts
\setmathfont{Latin Modern Math}
\usepackage{setspace}\setdisplayskipstretch{}
\usepackage{enumitem}
\usepackage{xparse}
\usepackage{environ}
\usepackage{keyval}
\usepackage{lipsum}
\usepackage{expkv-def}
\makeatletter
\ekvdefinekeys{myalignedat}
{
skip before skip = \myalignedat@before
,skip after skip = \myalignedat@after
}
\MHInternalSyntaxOn
\newcommand*\beginmyalignedat
{%
\ifvmode\else\\\@empty\fi
\noalign{\ifnum0=`}\fi
\myalignedat@
}
\newcommand\myalignedat@[2][]
{%
\ekvset{myalignedat}{#1}%
\vskip-\lineskiplimit
\vskip\normallineskiplimit
\vskip\myalignedat@before
\vbox\bgroup
\normalbaselines
\ifdim
\ifdim\@totalleftmargin=\z@
\linewidth
\else
-\maxdimen
\fi
=\columnwidth
\else
\parshape\@ne\@totalleftmargin\linewidth
\fi
\noindent
\centering
$\begin{alignedat}{#2}%
}
\newcommand\stopmyalignedat
{%
\end{alignedat}$%
\par
\vskip-\lineskiplimit
\vskip\normallineskiplimit
\vskip\myalignedat@after
\egroup
\ifnum0=`{\fi}%
}
\MHInternalSyntaxOff
\makeatother
\begin{document}
\onehalfspacing\KOMAoptions{DIV=current}
\lipsum[1][1-6]
\begin{flalign*}
& \text{For any numbers \(a\)}
&& \tag{P'10} \\
%%%%% the part related to the question %%%%%
\beginmyalignedat[after skip=12pt]{2}
& \text{(i) } & a &= b, \\
& \text{(ii) } & a &< b, \\
& \text{(iii) } & b &< a.
\stopmyalignedat
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) and \(b < c\), then \(a < c\).}
&& \tag{P'11} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\), then \(a + c < b + c\).}
&& \tag{P'12} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) abd \(0 < c\), then \(ac < bc\).}
&& \tag{P'13}
\end{flalign*}
\lipsum[2][1-8]
\end{document}
普通宏版本
一种宏变体,它以可扩展的方式抓取参数,使用可扩展的 key=value 接口,因此“直接”扩展为\adjintertext
,这样它的行为就像\adjintertext
在您的代码中使用一样。替代版本可以使用与其他解决方案相同的括号技巧来使用不可扩展的参数抓取和 key=value 解决方案。
语法是:
\myalignedat[<key=val>]{<alignedat-arg>}{<alignedat-content>}
完整代码:
\documentclass[oneside,DIV=12]{scrbook}
\usepackage{scrhack}
\usepackage[automark]{scrlayer-scrpage}
\usepackage[english]{babel}
\usepackage[babel]{microtype}
\usepackage{mathtools, amssymb}
\usepackage[warnings-off={mathtools-colon,mathtools-overbracket}]{unicode-math} % Math fonts
\setmathfont{Latin Modern Math}
\usepackage{setspace}\setdisplayskipstretch{}
\usepackage{enumitem}
\usepackage{xparse}
\usepackage{environ}
\usepackage{keyval}
\usepackage{lipsum}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% \adjintertext custom spacing \intertext macro (https://tex.stackexchange.com/a/280847/228055)
\MHInternalSyntaxOn
\newcommand{\adjintertext}[3]% #1=above skip, #2=below skip, #3=text
{\ifvmode\else\\\@empty\fi
\noalign{%
%\penalty\postdisplaypenalty\vskip\belowdisplayskip
\vskip-\lineskiplimit % CCS
\vskip\normallineskiplimit % CCS
\vskip#1
\vbox{\normalbaselines
\ifdim
\ifdim\@totalleftmargin=\z@
\linewidth
\else
-\maxdimen
\fi
=\columnwidth
\else \parshape\@ne \@totalleftmargin \linewidth
\fi
\noindent#3\par}%
%\penalty\predisplaypenalty\vskip\abovedisplayskip%
\vskip-\lineskiplimit % CCS
\vskip\normallineskiplimit % CCS
\vskip#2
}}%
\MHInternalSyntaxOff
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage{expkv-cs}
% grabbing an optional argument expandable
\NewExpandableDocumentCommand\myalignedat{O{} m m}
{%
% forward all arguments to \myalignedatKV
\myalignedatKV{#1}{#2}{#3}%
}
% splitting the first argument into two arguments based on a key=value interface
\ekvcSplitAndForward\myalignedatKV\myalignedatDO
{
before skip=0pt
,after skip=0pt
}
% grabbing all the arguments and setting the output
\newcommand\myalignedatDO[4]
{%
\adjintertext{#1}{#2}
{%
\centering
$%
\begin{alignedat}{#3}
#4%
\end{alignedat}%
$%
}%
}
\begin{document}
\onehalfspacing\KOMAoptions{DIV=current}
\lipsum[1][1-6]
\begin{flalign*}
& \text{For any numbers \(a\)}
&& \tag{P'10} \\
%%%%% the part related to the question %%%%%
\myalignedat[after skip=12pt]{2}
{
& \text{(i) } & a &= b, \\
& \text{(ii) } & a &< b, \\
& \text{(iii) } & b &< a.
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) and \(b < c\), then \(a < c\).}
&& \tag{P'11} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\), then \(a + c < b + c\).}
&& \tag{P'12} \\
& \text{For any numbers \(a\), \(b\), and \(c\), if \(a < b\) abd \(0 < c\), then \(ac < bc\).}
&& \tag{P'13}
\end{flalign*}
\lipsum[2][1-8]
\end{document}