这不是重复,而是 Ivo Welch 的问题引发的后续问题是否有针对用户(非专家)的 LaTeX 3 过渡指南?他询问是否有供用户使用的 LaTeX 3 过渡指南。
在 StackExchange 上,我注意到许多答案都包含Hooks
与描述新宏的选项和参数的奇怪方式相关的代码。Ivo 的一个答案提到了文档usrguide3
。跟进后,我发现interface3
和lthooks
手册都提到了。我已经设法做到了texdoc
这些,但对我来说,它们没有提供关于现在可以在 LaTeX 中做什么的集成用户级手册。
作为一名前软件开发人员,我知道首要任务是记录您所做的工作(如果您后来记不清为什么以及如何做,这确实很有帮助)。其次是向潜在用户解释如何利用您的杰出工作。
我强烈地感觉到,文档编制可能比原始工作花费更多的时间,但它非常重要。
我感谢所有为当前文档所付出的辛勤工作,但我希望很快就能开发出一些全面的用户级手册,解释并强调在当前 LaTeX 环境中可以做什么 — — 至少是针对用户(而不一定是包编写者)的usrguide3
、interfaces3
和lthooks
文档的连接。
答案1
LaTeX3(L3 编程层)接口\cs_new_protected:Npn
主要面向软件包编写者和高级用户。而 LaTeX2 接口则\NewDocumentCommand
面向普通用户。
对于高级用户(我将其定义为对 TeX 宏扩展有一定了解的用户),interface3
是一本全面且优秀的手册,在我看来,它或多或少足够了。
对于普通用户来说,由于目前LaTeX3只有一小部分提供了LaTeX2的接口,因此可供阅读的手册并不多:
xparse
:对于\NewDocumentCommand
,,\NewDocumentEnvironment
等等。xfp
:对于\inteval
和\fpeval
。lthooks
,ltcmdhooks
,ltfilehook
:对于\AddToHook
,,\RemoveFromHook
等等。
甚至 LaTeX3 中的整数步进函数都\int_step_inline
还没有提供 LaTeX2 接口,所以对于普通用户我建议使用其他现有的软件包例如pgffor
并耐心等待。
PS:另请参阅我打开的一个相关问题这里几天前。
2022-03-19 更新:我写了一篇新functional
基于 LaTeX3 编程层 ( expl3
) 的软件包。该软件包提供了易于使用的 LaTeX2 接口,可expl3
模拟其他编程语言(如Lua
或 )中的函数式编程JavaScript
。并且该软件包的手册包含的示例比 还多interface3
。我认为这个软件包对许多普通用户来说会很方便。
答案2
供参考
通过(非常快速的)原型教程式的实验性试用如下:
(它突出显示了令牌是什么)
通过俗话说的粗略计算,我估计需要花费大约 400 小时(对我来说:没有盲打等)。从字数上看,这差不多相当于一本中篇小说(12 号)。
幸运的是,expl3 的结构一致且合乎逻辑。
Expl3-coders:一个“原子”层(每个命令如何工作,+评论,练习),一个“场景”层(解决实际用例),以及也许是众所周知的食谱(项目等)。
我认为用户转换指南应该是最少的,因为 expl3 将是他们将要使用的用户命令的内部内容。
巧合的是,过去一周左右出现了大量解释性问题。
平均能量损失
\documentclass{article}
\usepackage{regexpatch}% also loads xparse
\usepackage{fontspec}
\newfontfamily\testfont{Noto Serif}
%\tracingxpatches
\usepackage{tcolorbox}
\tcbuselibrary{skins,xparse}
\tcbuselibrary{listings}
\DeclareTCBListing{mybox}{ s O{} m }{%
colback=red!3!white,
colframe=red!75!blue,
fonttitle=\sffamily\bfseries,
bicolor,
colbacklower=yellow!20,
segmentation style={double=white,draw=blue,
double distance=1pt,solid},
listing options={style=tcblatex,numbers=left,numberstyle=\small\color{blue!75!black},numbersep=5pt},%IfBooleanTF={#1}
%{listing side text}
%{text side listing},
title=\getegcounter\ -- #3,#2}
\ExplSyntaxOn
%\int_new:N \ic_eg_count
%\newcounter{egcounter}[section]
\newcounter{ic_eg_count}[section]
%\newcommand\getegcounter{\refstepcounter{egcounter}[\thesection.\theegcounter]}
\cs_new:Nn \ic_get_egnum: {
%\refstepcounter{egcounter}[\thesection.\theegcounter]
\refstepcounter{ic_eg_count}[\thesection.\theic_eg_count]
}
\cs_set_eq:NN \getegcounter \ic_get_egnum:
\int_new:N \g_commcount_int
\NewDocumentCommand { \comm } { } {
\textsc{Commentary:} ~
\int_gset:Nn \g_commcount_int { 0 }
}
\NewDocumentCommand { \cc } { } {
\par\noindent
( \int_gincr:N \g_commcount_int
{
\sffamily
\int_use:N \g_commcount_int
}
) ~
}
\NewDocumentCommand { \nocomm } { } {
-- ~
}
\NewDocumentCommand { \res } { } {
\par\textsc{Result:} ~
}
%\newcommand{\dobb}[1]{\renewcommand\ProcessedArgument{\fbox{\textcolor{blue}{\textbf{#1}}}}}
\cs_new:Npn \ic_do_bluebold: #1 {
\renewcommand\ProcessedArgument{\fbox{\textcolor{blue}{\textbf{#1}}}}
%\cs_gset:Nn \ic_processed_arg:n { \fbox{\textcolor{blue}{\textbf{#1}}} }
%\exp_args:NNo \cs_gset_eq:NN \ProcessedArgument \ic_processed_arg:n
}
\NewDocumentCommand { \dca } { s o >{\ic_do_bluebold:}m v } {
#4 ~ : ~
\IfBooleanTF {#1} {star} {no star}
;~ \IfNoValueTF {#2} {no opt = #2} {OArg = #2}
;~ MArg = #3.
% \par \#3 ~ after ~ pre-processing = #3
}
\NewDocumentEnvironment { codeillus } { +b } {
\begin{quotation}
\marginpar{\getegcounter}
\ttfamily
#1
\end{quotation}
}{}
%\newcommand\icmarker{ \% <------}
\cs_new:Nn \ic_marker: { \% ~ <------ }
\tl_new:N \l_ic_parm_tl
\NewDocumentCommand { \vv } { m O{\ic_marker:} } {
\tl_set:Nn \l_ic_parm_tl { #1 }
{
\bfseries
\color{blue}
% \detokenize { #1 }
\tl_to_str:N \l_ic_parm_tl
} ~~~ #2
}
\ExplSyntaxOff
\begin{document}
\section{Basics}
%=====================
\begin{codeillus}
\vv{\ExplSyntaxOn}
Some code.
Some code.
Some code.
\vv{\ExplSyntaxOff}
\end{codeillus}
%=====================
\begin{codeillus}
Some code.
\vv{\group_begin:}
Some code.
\vv{\group_end:}
Some code.
\end{codeillus}
%=====================
\begin{mybox}{Grouping}
\ExplSyntaxOn
Text ~ text ~
\group_begin:
\color{red}
\large
text ~ text ~
\group_end:
text ~ text.
\ExplSyntaxOff
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Defining a function}
\ExplSyntaxOn
\cs_new:Npn \ic_funca:n #1 { {\color{blue} \bfseries #1 } }
Text ~ \ic_funca:n { ~ This ~ is ~ blue ~ bold. } ~ Text.
\ExplSyntaxOff
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Defining a user command}
\ExplSyntaxOn
\NewDocumentCommand { \mycmdb } { m } {
\cs_new:Npn \ic_funcb:n ##1 { {\color{blue} \bfseries ##1 } }
\ic_funcb:n { #1 }
}
\ExplSyntaxOff
Text \mycmdb{This is blue bold.} Text.
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Using a token list}
\ExplSyntaxOn
\tl_new:N \l_myicc_tl
\NewDocumentCommand { \mycmdc } { m } {
\tl_set:Nn \l_myicc_tl { #1 }
\cs_new:Npn \ic_funcc:N ##1 { {\color{blue} \bfseries ##1 } }
\ic_funcc:N \l_myicc_tl
}
\ExplSyntaxOff
Text \mycmdc{This is blue bold.} Text.
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Appending to a token list}
\ExplSyntaxOn
\tl_new:N \l_myicd_tl
\NewDocumentCommand { \mycmdd } { m } {
\tl_set:Nn \l_myicd_tl { #1 }
\tl_put_right:Nn \l_myicd_tl { $\leftarrow$ ! ! }
\tl_put_left:Nn \l_myicd_tl { ! ! $\rightarrow$ }
\cs_new:Npn \ic_funcd:N ##1 { {\color{blue} \bfseries ##1 } }
\ic_funcd:N \l_myicd_tl
}
\ExplSyntaxOff
Text \mycmdd{This is blue bold.} Text.
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Modifying a token list (\texttt{\textbackslash tl\_replace})}
\ExplSyntaxOn
\tl_new:N \l_myice_tl
\NewDocumentCommand { \mycmde } { m } {
\tl_set:Nn \l_myice_tl { #1 }
\tl_replace_all:Nnn \l_myice_tl { blue } { not ~ red }
\tl_replace_all:Nnn \l_myice_tl { bold } { italic }
\cs_new:Npn \ic_funce:N ##1 { {\color{blue} \bfseries ##1 } }
\ic_funce:N \l_myice_tl
}
\ExplSyntaxOff
Text \mycmde{This is blue bold.} Text.
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Modifying a token list (\texttt{\textbackslash regex\_replace})}
\ExplSyntaxOn
\tl_new:N \l_myicez_tl
\NewDocumentCommand { \mycmdez } { m } {
\tl_set:Nn \l_myicez_tl { #1 }
\regex_replace_all:nnN
{ (\c{color} \cB\{ [^\cE\}]* \cE\}) }
{ not \ }
\l_myicez_tl
\regex_replace_all:nnN
{ \c{textbf}(.+)(bold) }
{ is\ \c{textsc}\1 small\ caps }
\l_myicez_tl
\tl_use:N \l_myicez_tl
}
\ExplSyntaxOff
Text \mycmdez{This is {\color{blue}blue} and \textbf{bold}.} Text.
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Copying a control sequence}
\ExplSyntaxOn
\cs_new:Npn \ic_funcf:N #1 { {\color{blue} \bfseries #1 } }
\cs_new_eq:NN \mybbcmd \ic_funcf:N
\ExplSyntaxOff
Text \mybbcmd{This is blue bold.} Text.
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Looping}
\ExplSyntaxOn
\cs_set:Npn \ic_funcg:n #1 { \symbol{#1} }
\cs_set:Nn \ic_funcgb: {
\int_step_function:nnnN { 97 } { 1 } { 122 } \ic_funcg:n
\
}
\cs_new_eq:NN \myloopcmd \ic_funcgb:
\ExplSyntaxOff
Text \myloopcmd Text.
\end{mybox}
\bigskip
\comm
\cc Open the expl3 environment.
\cc Define a 1-parameter function, g, that will print a glyph, given the glyph's slot number, in the current font using the symbol command.
\cc Define a no-parameter function, gb, that will
\cc step through values 97 to 122 (inclusive) and pass each value to the g function.
\cc Add a space (replacing the one gobbled after the command in the user code).
\cc \nocomm
\cc Create a user-command, \textbackslash myloopcmd, that will call the gb function.
\cc Close the expl3 environment.
\cc Use the user-command
\res The letters a..z are printed, followed by a space.
%\begin{quotation}
%>>\myloopcmd<<
%\end{quotation}
%=====================
\begin{mybox}{Mapping function}
\ExplSyntaxOn
\cs_set:Npn \ic_funch:n #1 { \fbox{#1}. }
\tl_new:N \l_myich_tl
\NewDocumentCommand { \mycmdh } { m } {
\tl_set:Nn \l_myich_tl { #1 }
\tl_map_function:NN \l_myich_tl \ic_funch:n
\par tl ~ = ~ >> \tl_use:N \l_myich_tl <<
}
\ExplSyntaxOff
Text \mycmdh{abc{de}fgh} Text.
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Mapping inline function}
\ExplSyntaxOn
\cs_set:Npn \ic_funci:n #1 { \fbox{#1}. }
\tl_new:N \l_myici_tl
\NewDocumentCommand { \mycmdi } { m } {
\tl_set:Nn \l_myici_tl { #1 }
\tl_map_inline:Nn \l_myici_tl { \ic_funci:n {##1} }
\par tl ~ = ~ >> \tl_use:N \l_myici_tl <<
}
\ExplSyntaxOff
Text \mycmdi{abc{de}fgh} Text.
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Contents of a token list (1)}
\ExplSyntaxOn
\tl_new:N \l_myicj_tl
\NewDocumentCommand { \mycmdj } { m } {
\tl_set:Nn \l_myicj_tl { #1 }
\par (a) ~ \tl_count:N \l_myicj_tl \ token ~ groups.
\par (b) ~ \tl_count_tokens:n { \l_myicj_tl } ~ token \int_compare:nNnTF {\tl_count_tokens:n { \l_myicj_tl }} = { 1 } { } { s }.
\par (c) ~ \exp_args:No \tl_count_tokens:n { \l_myicj_tl } ~ tokens: ~
{ \color{blue} \tl_to_str:N \l_myicj_tl }.
}
\ExplSyntaxOff
\mycmdj{abc{de}fgh}
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Contents of a token list (2)}
\ExplSyntaxOn
\tl_new:N \l_myick_tl
\NewDocumentCommand { \mycmdk } { m } {
\tl_set:Nn \l_myick_tl { #1 }
\par head: ~ \tl_head:N \l_myick_tl
\par tail: ~ \tl_tail:N \l_myick_tl
\par reverse: ~ \tl_reverse:N \l_myick_tl \tl_use:N \l_myick_tl ~<~ \tl_to_str:N \l_myick_tl
\par 5th ~ item : ~ \tl_item:Nn \l_myick_tl { 5 }
\par reverse: ~ \tl_reverse:N \l_myick_tl \tl_use:N \l_myick_tl ~<~ \tl_to_str:N \l_myick_tl
\par reverse ~ items: ~ \exp_args:No \tl_reverse_items:n { \l_myick_tl } ~<~ \tl_to_str:N \l_myick_tl
\par 5th ~ item : ~ \tl_item:Nn \l_myick_tl { 5 }
}
\ExplSyntaxOff
\mycmdk{abc{de}fgh}
\end{mybox}
\bigskip
%=====================
\begin{mybox}{Tokens}
\ExplSyntaxOn
\tl_new:N \l_myiclz_tl
\cs_set:Npn \ic_funcl:n #1 {
\tl_set:Nn \l_myiclz_tl { #1 }
\fbox{ \strut \tl_to_str:N \l_myiclz_tl } $^ \exp_args:No \tl_count_tokens:n { \l_myiclz_tl }$
\int_compare:nNnT
{ \exp_args:No \tl_count_tokens:n { \l_myiclz_tl } }
>
{ 1 }
{ >> \mycmdl{#1} << }
}
\tl_new:N \l_myicl_tl
\NewDocumentCommand { \mycmdl } { m } {
\tl_set:Nn \l_myicl_tl { #1 }
\tl_map_function:NN \l_myicl_tl \ic_funcl:n
}
\ExplSyntaxOff
\mycmdl{abc{d\textit{e}}fgh}
\end{mybox}
\bigskip
%\tl_item:Nn
*=====
\section{regexpatch}
\begin{mybox}{testca}
\newcommand{\testca}{\textit{label}}
Before: \testca
\par \regexpatchcmd{\testca}{\c{textit}}{\c{textbf}}{S}{F}
\par \xpatchcmd{\testca}{label}{babble}{S}{F}
\par After: \testca
\end{mybox}
\bigskip
%\xshowcmd*\ph
%\makeatletter
%\makeatother
%\tracingxpatches
\begin{mybox}{ph: Too many brace levels}
\newcommand{\ph}[1]{
\textbf{\textsc{{\color{blue}#1}}}\ \ }
Before: {\testfont\ph{Snail in the Bottle}}
\regexpatchcmd{\ph}{\c{color}\cB\{blue\cE\}}{red}{}{F}
\par After: {\testfont\ph{Snail in the Bottle}}
\end{mybox}
\bigskip
\tracingxpatches[0]
\begin{mybox}{ph2: Two levels of braces}
\newcommand{\phb}[1]{\textsc{{\color{blue}#1}}\ \ }
Before: {\testfont\phb{Snail in the Bottle}}
\regexpatchcmd{\phb}{\c{color}\cB\{blue\cE\}}{red}{}{F}
\par After: {\testfont\phb{Snail in the Bottle}}
\end{mybox}
\bigskip
\begin{mybox}{ph3: Entire \textbackslash color command replaced}
\newcommand{\phc}[1]{{
\bfseries\scshape\color{blue}#1\ \ }}
Before: {\testfont\phc{Snail in the Bottle}}
\regexpatchcmd{\phc}{\c{color}\cB\{blue\cE\}} {\c{color}\cB\{red\cE\}}{}{F}
\par After: {\testfont\phc{Snail in the Bottle}}
\end{mybox}
\bigskip
\begin{mybox}{ph4: Text replaced: `blue' > `red'}
\newcommand{\phd}[1]{{
\bfseries\scshape\color{blue}#1\ \ }}
Before: {\testfont\phd{Snail in the Bottle}}
\xpatchcmd{\phd}{blue}{red}{}{F}
\par After: {\testfont\phd{Snail in the Bottle}}
\end{mybox}
\bigskip
\begin{mybox}{ph5: Text (`blue') replaced by a macro (`\textbackslash mycolour')}
\newcommand{\mycolour}{green}
\newcommand{\phe}[1]{{
\bfseries\scshape\color{blue}#1\ \ }}
Before: {\testfont\phe{Snail in the Bottle}}
\regexpatchcmd{\phe}{blue}{\c{mycolour}}{}{F}
\par After: {\testfont\phe{Snail in the Bottle}}
\end{mybox}
\bigskip
\begin{mybox}{ph6: Multi-level grouping without braces\footnote{\sffamily\color{blue!5} ``patchable'' = it can be reconstructed from its decomposition under the current category code egime. -- Manual, \S 7.1 (2018/05/02)}}
\newcommand{\mycolour}{brown}
\newcommand{\phf}[1]{\begingroup
\bfseries\begingroup\scshape\begingroup\color{blue}#1\endgroup\ smallcaps\endgroup \ bold\endgroup\ normal \ \ }
Before: {\testfont\phf{Snail in the Bottle}}
\regexpatchcmd{\phf}{blue}{\c{mycolour}}{}{F}
\par After: {\testfont\phf{Snail in the Bottle}}
\end{mybox}
\bigskip
\dca{x}{\dca{x}}
\dca*{y}{\dca*{y}}
\dca[abc]{z}{\dca[abc]{z}}
\dca[xyz]{zz}{\dca[xyz]{zz}}
\dca*[xyzz]{zzz}{\dca*[xyzz]{zzz}}
\begin{mybox}{ph4a: Text replaced: `blue' > `red'}
\newcommand{\phda}[2]{{
\bfseries\scshape\color{blue}#1\normalcolor\ in the \color{blue}#2}}
Before: {\testfont\phda{Snail}{Bottle}}
\xpatchcmd{\phda}{blue}{red}{}{F}
\par After: {\testfont\phda{Snail}{Bottle}}
\end{mybox}
\bigskip
\begin{mybox}{ph4b: Text replaced: all `blue' > `red'}
\newcommand{\phdb}[2]{{
\bfseries\scshape\color{blue}#1\normalcolor\ in the \color{blue}#2}}
Before: {\testfont\phdb{Snail}{Bottle}}
\xpatchcmd*{\phdb}{blue}{red}{}{F}
\par After: {\testfont\phdb{Snail}{Bottle}}
\end{mybox}
\bigskip
\end{document}