![在列表环境中用“expl3”代码替换“\def”和“\@ifnextchar”](https://linux22.com/image/469964/%E5%9C%A8%E5%88%97%E8%A1%A8%E7%8E%AF%E5%A2%83%E4%B8%AD%E7%94%A8%E2%80%9Cexpl3%E2%80%9D%E4%BB%A3%E7%A0%81%E6%9B%BF%E6%8D%A2%E2%80%9C%5Cdef%E2%80%9D%E5%92%8C%E2%80%9C%5C%40ifnextchar%E2%80%9D.png)
我试图回答我在论坛上提出的另一个问题(如何修改shortlst中“\item”之间的水平和垂直间距?)关于shortlst
包裹,最后(我认为)不可能这样做。
话虽如此,我还是决定采用shortlst
宏并对其进行修改以实现这一点,基本上是一个水平枚举列表。我能够将大部分代码传递给expl3
,但是有几个宏和布尔值我无法转换。
代码是可以运行的(虽然与 hyperref 不兼容),但不是很简洁,具体来说:
% Internals (ugly mix plain TeX|expl3)
\def\sh@rtitem{
\endsh@rtitem
\int_incr:N \l_shenum_item_int
\int_gincr:N \g_shenum_item_int
\@ifnextchar [\sh@rt@item{\@noitemargtrue \sh@rt@item[\tl_use:N \l_shenum_label_tl]}}
\def\sh@rt@item[#1]{
\cs_set_eq:NN \endsh@rtitem \_end_short_item:
\legacy_if:nT {@noitemarg}
{
\legacy_if_set_false:n {@noitemarg}
\legacy_if:nT {@nmbrlist}
{
\refstepcounter{\@listctr}
}
}
\group_begin:
\lrbox{\l_shenum_item_text_box}
\makebox[\labelwidth][r]{#1}
\skip_horizontal:N \labelsep\ignorespaces
}
该shorlst
包没有重新定义item
通常的做法,而是采用了另一种技巧来实现列的效果,而我所有的尝试都失败了:(
以下是(非最小的)工作示例:
\documentclass{article}
\usepackage[showframe]{geometry}
\usepackage{amsmath}% , hyperref not work
% mypkg
\begin{filecontents*}[overwrite]{myshortenum.sty}
\NeedsTeXFormat{LaTeX2e}[2023-06-01]
\ProvidesExplPackage{myshortenum}{2023/10/13}{0.1}{Short Horizontal enumerate}
\ExplSyntaxOn
% vars
\newcounter{mycount}
\tl_new:N \l_shenum_counter_tl
\tl_set:Nn \l_shenum_counter_tl { mycount }
\tl_new:N \g_shenum_widest_label_tl
\tl_new:N \g_shenum_counter_styles_tl
\box_new:N \l_shenum_label_width_by_box
\dim_new:N \l_shenum_current_widest_dim
\int_new:N \l_shenum_level_int
\int_new:N \l_shenum_item_int
\int_new:N \g_shenum_item_int
\int_new:N \l_shenum_tmpa_int
\box_new:N \l_shenum_item_text_box
\bool_new:N \l_shenum_columns_sep_bool
\dim_new:N \l_shenum_columns_sep_dim
\dim_new:N \l_shenum_item_text_width_dim
\dim_new:N \l_shenum_tmpa_dim
% msg
\cs_generate_variant:Nn \msg_warning:nnn {nnV,nnx}
\msg_new:nnn { shenum } { nested }
{
The ~ environment ~ 'shortenumerate' ~ can't ~ be ~ nested ~ \msg_line_context:.
}
\msg_new:nnn { shenum } {item-too-wide}
{
Text ~ item ~ is ~ too ~ wide ~ > ~ #1 ~ pt ~\msg_line_context:. ~ \\
Use ~ '\c_backslash_str parbox[t]{\c_backslash_str itemwidth}{item ~ text}'.
}
% Counters and labels
\cs_new_protected:Npn \__shenum_register_counter_style:Nn #1 #2
{
\tl_const:cn { c_shenum_widest_ \cs_to_str:N #1 _tl } {#2}
\tl_gput_right:Nn \g_shenum_counter_styles_tl {#1}
}
\__shenum_register_counter_style:Nn \arabic { 0 }
\__shenum_register_counter_style:Nn \Alph { M }
\__shenum_register_counter_style:Nn \alph { m }
\__shenum_register_counter_style:Nn \Roman { VIII }
\__shenum_register_counter_style:Nn \roman { viii }
\cs_new_protected:Npn \__shenum_label_width_by_box:Nn #1#2
{
\hbox_set:Nn \l_shenum_label_width_by_box {#2}
\dim_set:Nn #1 { \box_wd:N \l_shenum_label_width_by_box }
}
\cs_new_protected:Npn \__shenum_label_style:Nnn #1#2#3
{
\tl_clear_new:N #1
\tl_put_right:Nx #1 { \tl_trim_spaces:n {#3} }
\tl_gset_eq:NN \g_shenum_widest_label_tl #1
\tl_map_inline:Nn \g_shenum_counter_styles_tl
{
\tl_replace_all:Nne #1 { ##1* } { \exp_not:N ##1 {#2} }
\tl_greplace_all:Nne \g_shenum_widest_label_tl { ##1* }
{ \tl_use:c { c_shenum_widest_ \cs_to_str:N ##1 _tl } }
}
\__shenum_label_width_by_box:Nn \l_shenum_current_widest_dim
{ \tl_use:N \g_shenum_widest_label_tl }
\tl_set_eq:cN { the #2 } #1
}
% keys
\keys_define:nn { shenum }
{
labelsep .dim_set:N = \l_shenum_labelsep_dim,
labelsep .initial:n = 0.5em,
labelwidth .dim_set:N = \l_shenum_labelwidth_dim,
label .code:n = {
\__shenum_label_style:Nnn \l_shenum_label_tl { \l_shenum_counter_tl } {#1}
\dim_set_eq:NN \l_shenum_labelwidth_dim \l_shenum_current_widest_dim
},
label .initial:n = \arabic*.,
topsep .skip_set:N = \l_shenum_topsep_skip,
topsep .initial:n = 5pt,
itemsep .skip_set:N = \l_shenum_itemsep_skip,
itemsep .initial:n = 3pt,
columns-sep .code:n = \bool_set_true:N \l_shenum_columns_sep_bool
\dim_set:Nn \l_shenum_columns_sep_dim {#1},
columns .int_set:N = \l_shenum_columns_int,
columns .initial:n = 2,
}
% Set internal for columns and \itemwidth
\cs_new_protected:Nn \__shenum_default_columns_set:
{
\bool_if:NF \l_shenum_columns_sep_bool
{
\dim_set:Nn \l_shenum_columns_sep_dim
{
( \l_shenum_labelwidth_dim + \l_shenum_labelsep_dim ) / 2
}
}
\dim_set:Nn \l_shenum_item_text_width_dim
{
\linewidth / \l_shenum_columns_int -\labelwidth -\labelsep
}
% For use in \parbox | minipage
\dim_zero_new:N \itemwidth
\dim_set:Nn \itemwidth
{
\linewidth / \l_shenum_columns_int -\labelwidth -\labelsep -0.5\l_shenum_columns_sep_dim
}
}
% Set second arg in list
\cs_new_protected:Nn \__shenum_list_second_arg:
{
\skip_set_eq:NN \topsep \l_shenum_topsep_skip
\skip_set_eq:NN \parsep \l_shenum_itemsep_skip
\skip_zero:N \itemsep
\skip_zero:N \partopsep
\dim_add:Nn \leftmargin { \itemindent - \labelwidth - \labelsep }
\dim_zero:N \itemindent
\dim_zero:N \listparindent
\dim_set_eq:NN \labelwidth \l_shenum_labelwidth_dim
\dim_set_eq:NN \labelsep \l_shenum_labelsep_dim
}
% Remove extra \parsep when items fit exact \item's / columns = exact
\cs_new_protected:Nn \__shenum_remove_extra_parsep:
{
\int_compare:nNnT
{ \int_mod:nn { \g_shenum_item_int } { \l_shenum_columns_int } } = { \c_zero_int }
{
\skip_sub:Nn \parsep { \topsep }
\par\addvspace{-\parsep }
\int_gzero:N \g_shenum_item_int
}
}
% env
\NewDocumentEnvironment{shortenumerate}{ O{} }
{
\int_incr:N \l_shenum_level_int
\int_compare:nNnT { \l_shenum_level_int } > { 1 }
{
\msg_error:nn { shenum } { nested }
}
\IfValueT{#1}{ \keys_set:nn { shenum } {#1} }
\list{ }{ \__shenum_list_second_arg: }
\__shenum_default_columns_set:
\item \scan_stop:
\cs_set_eq:NN \endsh@rtitem \noindent
\cs_set_eq:NN \item \sh@rtitem
\usecounter{mycount}
}
{
\endsh@rtitem
\__shenum_remove_extra_parsep:
\endlist
}
% Internals (ugly mix plain TeX|expl3)
\def\sh@rtitem{
\endsh@rtitem
\int_incr:N \l_shenum_item_int
\int_gincr:N \g_shenum_item_int
\@ifnextchar [\sh@rt@item{\@noitemargtrue \sh@rt@item[\tl_use:N \l_shenum_label_tl]}}
\def\sh@rt@item[#1]{
\cs_set_eq:NN \endsh@rtitem \_end_short_item:
\legacy_if:nT {@noitemarg}
{
\legacy_if_set_false:n {@noitemarg}
\legacy_if:nT {@nmbrlist}
{
\refstepcounter{\@listctr}
}
}
\group_begin:
\lrbox{\l_shenum_item_text_box}
\makebox[\labelwidth][r]{#1}
\skip_horizontal:N \labelsep\ignorespaces
}
% Internal
\cs_new_protected:Nn \_end_short_item:
{
\endlrbox
\group_end:
\dim_set:Nn \l_shenum_tmpa_dim { \box_wd:N \l_shenum_item_text_box }
\dim_compare:nNnT { \l_shenum_tmpa_dim } > { \l_shenum_item_text_width_dim + \labelwidth + \labelsep }
{
\msg_warning:nnx { shenum } {item-too-wide} { \dim_to_decimal:n { \itemwidth } }
}
\box_set_wd:Nn \l_shenum_item_text_box
{
\l_shenum_item_text_width_dim + \labelwidth + \labelsep - 0.5\l_shenum_columns_sep_dim
}
\hbadness=10000
\box_use:N \l_shenum_item_text_box
\int_compare:nNnTF { \l_shenum_item_int } = { \l_shenum_columns_int }
{
\hspace{\l_shenum_columns_sep_dim }
\par\noindent
\int_zero:N \l_shenum_item_int
}
{ \hspace{\l_shenum_columns_sep_dim } }
}
\ExplSyntaxOff
\endinput
\end{filecontents*}
\usepackage{myshortenum}
\setlength{\parskip}{0pt}
\setlength{\parindent}{0pt}
\begin{document}
Above
\begin{shortenumerate}[label=(\arabic*.),columns=2,columns-sep=20pt,itemsep=3pt,topsep=1cm]
\item \parbox[t]{\itemwidth}{A little paragraph that will be too long to fit on one line no matter what ands no more lightsss\strut.}
\item A short item A short item A short item.\label{A}
\item $b_n = \frac{(n^3 - 5n)^4 - n^{12}}{n^{11}}$
\item $c_n = \frac{n^{n + 1}}{n!}$\strut
\item $e_n = \frac{2^{(n^3)}}{n!5^{(n^2)} - n^n}$
\item $f_n = \sqrt{n + \sqrt{2n}} - \sqrt{n + \sqrt{2n}}x$
\end{shortenumerate}
Below \ref{A}
Above
\begin{shortenumerate}[columns=2]
\item \parbox[t]{\itemwidth}{A little paragraph that will be too long to fit on one line no matter what ands no more lightsss\strut.}
\item A short item A short item A short item.\label{B}
\item $b_n = \frac{(n^3 - 5n)^4 - n^{12}}{n^{11}}$
\item $c_n = \frac{n^{n + 1}}{n!}$\strut
\item $f_n = \sqrt{n + \sqrt{2n}} - \sqrt{n + \sqrt{2n}}x$
\end{shortenumerate}
Below \ref{B}
\end{document}
输出的图像(这正是我们所期望的):
我可以去掉\def
,\@ifnextchar
或者用其他方法(\item
例如重新定义)吗?
答案1
这是我的疑问的答案,感谢 David Carlisle、Joseph Wright 和 Ulrich Diez 的评论,这个问题终于解决了。我能够删除\def
,\@ifnextchar
现在它与 兼容了hyperref
:D。代码如下所示:
\documentclass{article}
\usepackage[showframe]{geometry}
\usepackage{amsmath}%
% mypkg
\begin{filecontents*}[overwrite]{myshortenum.sty}
\NeedsTeXFormat{LaTeX2e}[2023-06-01]
\ProvidesExplPackage{myshortenum}{2023/10/14}{0.2}{Short Horizontal enumerate}
\ExplSyntaxOn
% vars
\newcounter{mycount}
\tl_new:N \l_shenum_counter_tl
\tl_set:Nn \l_shenum_counter_tl { mycount }
\bool_new:N \g_shenum_hyperref_bool
\tl_new:N \g_shenum_widest_label_tl
\tl_new:N \g_shenum_counter_styles_tl
\box_new:N \l_shenum_label_width_by_box
\dim_new:N \l_shenum_current_widest_dim
\int_new:N \l_shenum_level_int
\int_new:N \l_shenum_item_int
\int_new:N \g_shenum_item_int
\int_new:N \l_shenum_tmpa_int
\box_new:N \l_shenum_item_text_box
\bool_new:N \l_shenum_columns_sep_bool
\dim_new:N \l_shenum_columns_sep_dim
\dim_new:N \l_shenum_item_text_width_dim
\dim_new:N \l_shenum_tmpa_dim
% msg
\cs_generate_variant:Nn \msg_warning:nnn {nnV,nnx}
\msg_new:nnn { shenum } { nested }
{
The ~ environment ~ 'shortenumerate' ~ can't ~ be ~ nested ~ \msg_line_context:.
}
\msg_new:nnn { shenum } {item-too-wide}
{
Text ~ item ~ is ~ too ~ wide ~ > ~ #1 ~ pt ~\msg_line_context:. ~ \\
Use ~ '\c_backslash_str parbox[t]{\c_backslash_str itemwidth}{item ~ text}'.
}
% hyperref
\hook_gput_code:nnn { begindocument } { myshortenum } { \__shenum_after_hyperref: }
\hook_gset_rule:nnnn { begindocument } { myshortenum } { after } { hyperref }
\cs_new_protected:Nn \__shenum_after_hyperref:
{
\IfPackageLoadedTF { hyperref }
{ \bool_gset_true:N \g_shenum_hyperref_bool } { }
}
% Counters and labels
\cs_new_protected:Npn \__shenum_register_counter_style:Nn #1 #2
{
\tl_const:cn { c_shenum_widest_ \cs_to_str:N #1 _tl } {#2}
\tl_gput_right:Nn \g_shenum_counter_styles_tl {#1}
}
\__shenum_register_counter_style:Nn \arabic { 0 }
\__shenum_register_counter_style:Nn \Alph { M }
\__shenum_register_counter_style:Nn \alph { m }
\__shenum_register_counter_style:Nn \Roman { VIII }
\__shenum_register_counter_style:Nn \roman { viii }
\cs_new_protected:Npn \__shenum_label_width_by_box:Nn #1#2
{
\hbox_set:Nn \l_shenum_label_width_by_box {#2}
\dim_set:Nn #1 { \box_wd:N \l_shenum_label_width_by_box }
}
\cs_new_protected:Npn \__shenum_label_style:Nnn #1#2#3
{
\tl_clear_new:N #1
\tl_put_right:Nx #1 { \tl_trim_spaces:n {#3} }
\tl_gset_eq:NN \g_shenum_widest_label_tl #1
\tl_map_inline:Nn \g_shenum_counter_styles_tl
{
\tl_replace_all:Nne #1 { ##1* } { \exp_not:N ##1 {#2} }
\tl_greplace_all:Nne \g_shenum_widest_label_tl { ##1* }
{ \tl_use:c { c_shenum_widest_ \cs_to_str:N ##1 _tl } }
}
\__shenum_label_width_by_box:Nn \l_shenum_current_widest_dim
{ \tl_use:N \g_shenum_widest_label_tl }
\bool_if:NTF \g_shenum_hyperref_bool
{
\tl_set_eq:cN { theH #2 } #1
}
{ \tl_set_eq:cN { the #2 } #1 }
}
% keys
\keys_define:nn { shenum }
{
labelsep .dim_set:N = \l_shenum_labelsep_dim,
labelsep .initial:n = 0.5em,
labelwidth .dim_set:N = \l_shenum_labelwidth_dim,
label .code:n = {
\__shenum_label_style:Nnn \l_shenum_label_tl { \l_shenum_counter_tl } {#1}
\dim_set_eq:NN \l_shenum_labelwidth_dim \l_shenum_current_widest_dim
},
label .initial:n = \arabic*.,
topsep .skip_set:N = \l_shenum_topsep_skip,
topsep .initial:n = 5pt,
itemsep .skip_set:N = \l_shenum_itemsep_skip,
itemsep .initial:n = 3pt,
columns-sep .code:n = \bool_set_true:N \l_shenum_columns_sep_bool
\dim_set:Nn \l_shenum_columns_sep_dim {#1},
columns .int_set:N = \l_shenum_columns_int,
columns .initial:n = 2,
}
% Set internal for columns and \itemwidth
\cs_new_protected:Nn \__shenum_default_columns_set:
{
\bool_if:NF \l_shenum_columns_sep_bool
{
\dim_set:Nn \l_shenum_columns_sep_dim
{
( \l_shenum_labelwidth_dim + \l_shenum_labelsep_dim ) / 2
}
}
\dim_set:Nn \l_shenum_item_text_width_dim
{
\linewidth / \l_shenum_columns_int -\labelwidth -\labelsep
}
% For use in \parbox | minipage
\dim_zero_new:N \itemwidth
\dim_set:Nn \itemwidth
{
\linewidth / \l_shenum_columns_int -\labelwidth -\labelsep -0.5\l_shenum_columns_sep_dim
}
}
% Set second arg in list
\cs_new_protected:Nn \__shenum_list_second_arg:
{
\skip_set_eq:NN \topsep \l_shenum_topsep_skip
\skip_set_eq:NN \parsep \l_shenum_itemsep_skip
\skip_zero:N \itemsep
\skip_zero:N \partopsep
\dim_add:Nn \leftmargin { \itemindent - \labelwidth - \labelsep }
\dim_zero:N \itemindent
\dim_zero:N \listparindent
\dim_set_eq:NN \labelwidth \l_shenum_labelwidth_dim
\dim_set_eq:NN \labelsep \l_shenum_labelsep_dim
}
% Remove extra \parsep when items fit exact \item's / columns = exact
\cs_new_protected:Nn \__shenum_remove_extra_parsep:
{
\int_compare:nNnT
{ \int_mod:nn { \g_shenum_item_int } { \l_shenum_columns_int } } = { \c_zero_int }
{
\skip_sub:Nn \parsep { \topsep }
\par\addvspace{-\parsep }
\int_gzero:N \g_shenum_item_int
}
}
% env
\NewDocumentEnvironment{shortenumerate}{ o }
{
\int_incr:N \l_shenum_level_int
\int_compare:nNnT { \l_shenum_level_int } > { 1 }
{
\msg_error:nn { shenum } { nested }
}
\IfValueT{#1}{ \keys_set:nn { shenum } {#1} }
\list{ }{ \__shenum_list_second_arg: }
\__shenum_default_columns_set:
\item \scan_stop:
\cs_set_eq:NN \_shenum_end_item_tmp: \noindent
\cs_set_eq:NN \item \__shenum_item:
\usecounter{mycount}
}
{
\_shenum_end_item_tmp:
\__shenum_remove_extra_parsep:
\endlist
}
% Internals (ugly mix legacy 2e and expl3)
\cs_set_nopar:Nn \__shenum_item:
{
\int_incr:N \l_shenum_item_int
\int_gincr:N \g_shenum_item_int
\peek_remove_spaces:n
{
\_shenum_end_item_tmp:
\peek_meaning:NTF [
{
\__shenum_item:w
}
{
\legacy_if_set_true:n {@noitemarg}
\__shenum_item:w [\tl_use:N \l_shenum_label_tl]
}
}
}
\cs_set_nopar:Npn \__shenum_item:w [#1]
{
\cs_set_eq:NN \_shenum_end_item_tmp: \_shenum_end_item:
\legacy_if:nT {@noitemarg}
{
\legacy_if_set_false:n {@noitemarg}
\legacy_if:nT {@nmbrlist}
{
\refstepcounter{\@listctr}
}
}
\group_begin:
\lrbox{\l_shenum_item_text_box}
\makebox[\labelwidth][r]{#1}
\skip_horizontal:N \labelsep\ignorespaces
}
% Internal
\cs_set_nopar:Nn \_shenum_end_item:
{
\endlrbox
\group_end:
\dim_set:Nn \l_shenum_tmpa_dim { \box_wd:N \l_shenum_item_text_box }
\dim_compare:nNnT { \l_shenum_tmpa_dim } > { \l_shenum_item_text_width_dim + \labelwidth + \labelsep }
{
\msg_warning:nnx { shenum } {item-too-wide} { \dim_to_decimal:n { \itemwidth } }
}
\box_set_wd:Nn \l_shenum_item_text_box
{
\l_shenum_item_text_width_dim + \labelwidth + \labelsep - 0.5\l_shenum_columns_sep_dim
}
\hbadness=10000
\box_use:N \l_shenum_item_text_box
\int_compare:nNnTF { \l_shenum_item_int } = { \l_shenum_columns_int }
{
\hspace{\l_shenum_columns_sep_dim }
\par\noindent
\int_zero:N \l_shenum_item_int
}
{ \hspace{\l_shenum_columns_sep_dim } }
}
\ExplSyntaxOff
\endinput
\end{filecontents*}
\usepackage{myshortenum}
\usepackage{hyperref}
\setlength{\parskip}{0pt}
\setlength{\parindent}{0pt}
\begin{document}
Above
\begin{shortenumerate}[label=(\arabic*.),columns=2,columns-sep=20pt,itemsep=3pt,topsep=1cm]
\item \parbox[t]{\itemwidth}{A little paragraph that will be too long to fit on one line no matter what ands no more lightsss\strut.}
\item A short item A short item A short item.\label{A}
\item $b_n = \frac{(n^3 - 5n)^4 - n^{12}}{n^{11}}$
\item $c_n = \frac{n^{n + 1}}{n!}$\strut
\item $e_n = \frac{2^{(n^3)}}{n!5^{(n^2)} - n^n}$
\item $f_n = \sqrt{n + \sqrt{2n}} - \sqrt{n + \sqrt{2n}}x$
\end{shortenumerate}
Below \ref{A}
Above
\begin{shortenumerate}[columns=2]
\item \parbox[t]{\itemwidth}{A little paragraph that will be too long to fit on one line no matter what ands no more lightsss\strut.}
\item A short item A short item A short item.\label{B}
\item $b_n = \frac{(n^3 - 5n)^4 - n^{12}}{n^{11}}$
\item $c_n = \frac{n^{n + 1}}{n!}$\strut
\item $f_n = \sqrt{n + \sqrt{2n}} - \sqrt{n + \sqrt{2n}}x$
\end{shortenumerate}
Below \ref{B}
\end{document}
它比的原始版本更加通用,shortlst
但不支持footnote
,并且还有一些可以以更好的方式编写的东西,例如:
\legacy_if:nT {@noitemarg}
{
\legacy_if_set_false:n {@noitemarg}
\legacy_if:nT {@nmbrlist}
{
\refstepcounter{\@listctr}
}
}
但我想这需要重新定义\item
和\makelabel
。由于没有其他用户的回应,我暂时接受我的。