尝试理解我发现的一段代码

尝试理解我发现的一段代码

在这个帖子中,我发现了一段非常有趣且有用的代码,但我不知道它是如何工作的:https://tex.stackexchange.com/a/584979/261033

以下是有问题的代码,供参考:

\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\def\matr#1{\def\matrL{}\matrA#1\end}
\def\matrA#1{\ifx\end#1\pmatrix{\matrL}\else 
   \ifx,#1\addto\matrL{&}\else
   \ifx;#1\addto\matrL{\cr}\else
   \addto\matrL{#1}\fi\fi
   \expandafter\matrA\fi
}

它可以被调用为\matr{a,b;c,d}并生成一个 pmatrix。如果你经常使用简单的矩阵并且厌倦了反复输入完整的长命令,那么我认为它非常有用。有人还指出,要使其与 amsmath 包一起使用,你需要将 替换\pmatrix{\matrL}\begin{pmatrix}\matrL\end{pmatrix}

无论如何,我希望对该命令进行进一步的修改,例如使用不同的符号作为行/列分隔符,使输入按列解析,或者将其概括为所有矩阵括号类型(因此您可以输入类似的内容,\matr{[)}{...}它将生成一个带有特定括号集的矩阵,类似于\left[\begin{matrix}...\end{matrix}\right)),但我还不知道如何执行任何操作。

我尝试了解这些命令,但很快就遇到了问题。其中一些命令(例如)似乎\addto太晦涩难懂,以至于任何地方都没有关于它们的文档!Google 只能找到类似\addtocontents或的内容\g@addto@macro类的命令,而这些命令我并不感兴趣,而且我也找不到任何全面的 TeX 资源来列出它们,甚至没有https://www.latex-project.org或者https://texdoc.org/index.html

所以,如果有人能帮我解决这个问题,或者能给我指出这些命令的一些实际存在的文档,我将不胜感激。提前谢谢您。

答案1

\addto宏将代码附加到另一个宏。

如果你有\def\foo{X}并且愿意这样做\addto\foo{Y},TeX 就可以做到

\expandafter\def\expandafter\foo\expandafter{\foo Y}

结果是

\def\foo{XY}

之后\expandafter在链条完成工作不能\addto如果您计划在 LaTeX 中使用代码,请使用,因为\addto这是使用的重要命令,babel如果您重新定义它,则可能会带来灾难。只需使用另一个名称即可。

做什么\matr?它执行\def\matrL{},初始化矩阵主体的容器。接下来,它将参数传递给\matrA,开始递归。

该宏\matrA只是逐一读取以下标记:

  • 如果下一个标记为,(表示单元格的结束),则将&其附加到\matrL
  • 如果下一个标记为;(表示一行的结束),则将\cr其附加到\matrL
  • 如果下一个标记是\end,则运行残局,即\pmatrix{\matrL},并且递归停止;
  • 在所有其他情况下,令牌仅附加到\matrL

您能将其适配到 LaTeX 和特定的pmatrix环境中吗?是的,很容易。

首先,为内部宏选择不同的名称,最好@在其中使用。

\documentclass{article}
\usepackage{amsmath}

\makeatletter
\newcommand{\matr}[1]{\def\tsskyx@matrL{}\tsskyx@matrA#1\end}

\def\tsskyx@addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}

\def\tsskyx@matrA#1{%
  \ifx\end#1%
    \begin{pmatrix}\tsskyx@matrL\end{pmatrix}%
  \else 
    \ifx,#1%
      \tsskyx@addto\tsskyx@matrL{&}%
    \else
      \ifx;#1%
        \tsskyx@addto\tsskyx@matrL{\\}%
      \else
        \tsskyx@addto\tsskyx@matrL{#1}%
      \fi
    \fi
    \expandafter\tsskyx@matrA
  \fi
}
\makeatother

\begin{document}

\[
\matr{a,b;c,d}
\]

\end{document}

相同的代码,但差别很小:\cr我们使用附加\\\begin{pmatrix}...\end{pmatrix}不是使用。

使用 的不同实现expl3,其中可选参数指定分隔符(默认p,可以bvVBamsmath约定一致)。

\documentclass{article}
\usepackage{amsmath}

\ExplSyntaxOn

\NewDocumentCommand{\matr}{O{p}m}
 {
  \tsskyx_matr:nn { #1 } { #2 }
 }

\seq_new:N \l__tsskyx_matr_body_seq

\cs_new_protected:Nn \tsskyx_matr:nn
 {
  % build the matrix
  \begin{#1matrix}
  % split the argument at ;
  \seq_set_split:Nnn \l__tsskyx_matr_body_seq { ; } { #2 }
  % map the items, that are comma separated lists
  \seq_map_function:NN \l__tsskyx_matr_body_seq \__tsskyx_matr_row:N
  \end{#1matrix}
 }

\cs_new_protected:Nn \__tsskyx_matr_row:N
 {
  \clist_use:nn { #1 } { & } \\
 }

\ExplSyntaxOff

\begin{document}

\[
\matr{a,b;c,d}
\qquad
\matr[b]{a,b;c,d}
\]

\end{document}

在此处输入图片描述

如果您还想更改行和列分隔符,则可以使用键值系统。

\documentclass{article}
\usepackage{amsmath}

\ExplSyntaxOn

\NewDocumentCommand{\matr}{O{}m}
 {
  \group_begin:
  \keys_set:nn { tsskyx/matr } { #1 }
  \tsskyx_matr:n { #2 }
  \group_end:
 }

\seq_new:N \l__tsskyx_matr_body_seq
\seq_new:N \l__tsskyx_matr_row_seq

\keys_define:nn { tsskyx/matr }
 {
  row-sep .tl_set:N  = \l__tsskyx_matr_rowsep_tl,
  col-sep .tl_set:N  = \l__tsskyx_matr_colsep_tl,
  delim   .tl_set:N  = \l__tsskyx_matr_delim_tl,
  row-sep .initial:n = { ; },
  col-sep .initial:n = { , },
  delim   .initial:n = { p },
 }

\cs_new_protected:Nn \tsskyx_matr:n
 {
  % build the matrix
  \begin{\l__tsskyx_matr_delim_tl matrix}
  % split the argument at ;
  \seq_set_split:NVn \l__tsskyx_matr_body_seq \l__tsskyx_matr_rowsep_tl { #1 }
  % map the items, that are comma separated lists
  \seq_map_function:NN \l__tsskyx_matr_body_seq \__tsskyx_matr_row:N
  \end{\l__tsskyx_matr_delim_tl matrix}
 }

\cs_new_protected:Nn \__tsskyx_matr_row:N
 {
  \seq_set_split:NVn \l__tsskyx_matr_row_seq \l__tsskyx_matr_colsep_tl { #1 }
  \seq_use:Nn \l__tsskyx_matr_row_seq { & } \\
 }

\ExplSyntaxOff

\begin{document}

\begin{gather*}
\matr{a,b;c,d}
\\
\matr[delim=b]{a,b;c,d}
\\
\matr[row-sep=\\,col-sep=&,delim=V]{a & b \\ c & d}
\\
\matr[row-sep=;,col-sep=:]{a:b;c:d}
\end{gather*}

\end{document}

在此处输入图片描述

答案2

\addto代码中提到的宏来自最初为 csplain 设计的 OPmac 宏,后来 OPmac 为纯 TeX 设计,现在用于 OpTeX。这些宏包的作者不支持 LaTeX。他的宏主要用于纯 TeX,因为它们主要基于 TeX 原语,所以它们也适用于 LaTeX。但有时会有一些小障碍,例如名称与\addtoLaTeX 包中的同名名称相同。因为作者不使用 LaTeX 或其包(如 babel),所以这对他和其他纯 TeX 用户来说没有问题。

如果要修改宏以便能够在参数中设置矩阵的括号类型,则可以将给定的类型发送到\matrAdefined的定义中里面\matr。因此,你有\def一个\def技巧:

\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\def\matr#1#2#3{\def\matrL{}%
   \def\matrA##1{\ifx\end##1\left#1\matrix{\matrL}\right#2\else 
      \ifx,##1\addto\matrL{&}\else
      \ifx;##1\addto\matrL{\cr}\else
      \addto\matrL{##1}\fi\fi
      \expandafter\matrA\fi
   }
   \matrA#3\end
}

Test and usage:
$$
  \matr[]{a,b;c,d}
$$

\bye

编辑为了与此处的另一个答案进行比较,我展示了如何使用 OpTeX 中的键值宏:

\def\matrdefaults{%
   delims=(),   % delimiters of the matrix
   row-sep={;}, % row separator used in parameter
   col-sep={,}, % column separator used in parameter
}
\optdef\matr [] #1{{%
   \readkv\matrdefaults \readkv{\the\opt}%
   \ea\matrX \expanded{\kv{delims}\kv{row-sep}\kv{col-sep}}#1\end
}}
\def\matrX#1#2#3#4{\def\matrL{}%
   \def\matrA##1{\ifx\end##1\left#1\matrix{\matrL}\right#2\else 
      \ifx#4##1\addto\matrL{&}\else
      \ifx#3##1\addto\matrL{\cr}\else
      \addto\matrL{##1}\fi\fi
      \ea\matrA\fi
   }
   \matrA
}

$$
  \displaylines{
     \matr {a,b;c,d} \cr
     \matr [delims={[]}] {a,b;c,d} \cr
     \matr [row-sep={\cr}, col-sep={&}, delims={\|\|}] {a&b\cr c&d} \cr
     \matr [row-sep=;,col-sep=:]{a:b;c:d}
}
$$

\bye

如果你想用这个宏创建你的包,那么你应该使用一个单独的命名空间(tsskyx这里)。这个命名空间中的控制序列以 为前缀.,其他使用的控制序列(原语和 OpTeX 宏)以 为前缀_。这样你的宏就与用户命名空间(无前缀名称)和其他具有不同命名空间的宏包隔离开来。

\_namespace{tsskyx}
\_def\.matrdefaults{%
   delims=(),   % delimiters of the matrix
   row-sep={;}, % row separator used in parameter
   col-sep={,}, % column separator used in parameter
}
\_optdef\.matr [] #1{{%
   \_kvdict{tsskyx}%
   \_readkv\.matrdefaults \_readkv{\_the\_opt}%
   \_ea\.matrX \_expanded{\_kv{delims}\_kv{row-sep}\_kv{col-sep}}#1\_end
}}
\_def\.matrX#1#2#3#4{\_def\.matrL{}%
   \_def\.matrA##1{\_ifx\_end##1\_left#1\_matrix{\.matrL}\_right#2\_else 
      \_ifx#4##1\_addto\.matrL{&}\_else
      \_ifx#3##1\_addto\.matrL{\_cr}\_else
      \_addto\.matrL{##1}\_fi\_fi
      \_ea\.matrA\_fi
   }
   \.matrA
}
\_nspublic \matr ;
\_endnamespace

$$
  \displaylines{
     \matr {a,b;c,d} \cr
     \matr [delims={[]}] {a,b;c,d} \cr
     \matr [row-sep={\cr}, col-sep={&}, delims={\|\|}] {a&b\cr c&d} \cr
     \matr [row-sep=;,col-sep=:]{a:b;c:d}
}
$$

\bye

相关内容