我正在尝试编写一个包来提供一些常见线性代数运算的快捷命令(至少对于我的线性代数入门课程而言)。但是,在某些情况下,我遇到了奇怪的错误。根据其他帖子,我认为我被脆弱与强大的命令问题所困扰(我不完全理解),而且我不确定如何修复它。我尝试将我的宏更改为\DeclareRobustCommand
并在各个地方添加\protect
s,但这些都无法解决问题。具体来说,我遇到了以下错误:
Use of \\mat doesn't match its definition.
\@ifnextchar ... \reserved@d =#1\def \reserved@a {
#2}\def \reserved@b {#3}\f...
l.136 This \mat{\sqrt{2},0,0,0}
also doesn't work
If you say, e.g., `\def\a1{...}', then you must always
put `1' after `\a', since control sequence names are
made up of letters only. The macro here has not been
followed by the required stuff, so I'm ignoring it.
这是错误的 MRCE(las
代表“线性代数捷径”):
\documentclass{article}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsfonts}
\usepackage{tcolorbox}
\usepackage{etoolbox}
\usepackage{pgffor}
\usepackage{pgfmath}
\usepackage{calc}
\usepackage{intcalc}
\usepackage{calculator}
\usepackage{nicematrix}
\makeatletter
\setcounter{MaxMatrixCols}{25}
\gdef\las@defaultvec{row}
\newcounter{las@matrow}
\newcounter{las@matcol}
\newcounter{las@listlen}
\newcounter{las@matsize}
\newcommand{\las@length}[2]{ % \len{list}{counter}
\setcounter{#2}{0}
\foreach\@e in {#1} {
\stepcounter{#2}
}
}
\newcommand{\las@createMatrix}[3]{ % dims, rows
\setcounter{las@matrow}{#1}
\setcounter{las@matcol}{#2}
\setcounter{las@listlen}{0}
\las@length{#3}{las@listlen} % get the length of the list after expansion
\setcounter{las@matsize}{\thelas@matrow*\thelas@matcol}
\ifnum\thelas@matsize=\thelas@listlen % if neq
\else
\PackageError{LAS}{Attempt to define a \arabic{las@matrow} by \arabic{las@matcol} matrix with \arabic{las@listlen} elemets}{}
\fi
\xdef\las@resMatrix{}
\foreach \elem[count=\@i] in {#3}{
% \xdef\las@resMatrix{\las@resMatrix \elem \\}
\ifnum\@i=\thelas@matsize
\xdef\las@resMatrix{\las@resMatrix\elem}
\else
\ifnum\intcalcMod{\@i}{\thelas@matcol}=0
\xdef\las@resMatrix{\las@resMatrix \elem \\}
\else
\xdef\las@resMatrix{\las@resMatrix \elem &}
\fi
\fi
}
}
\newcommand{\las@vec}[2]{
\las@length{#2}{las@listlen}
\ifstrequal{#1}{row}{
\las@createMatrix{1}{\thelas@listlen}{#2}
}{
\ifstrequal{#1}{col}{
\las@createMatrix{\thelas@listlen}{1}{#2}
}{
\PackageError{LAS}{Unknown option "#1". Valid options are "col" and "row"}{}
}
}
}
\newcommand{\las@mat}[2]{
\setcounter{las@matrow}{1}
\setcounter{las@matcol}{1}
\ifstrequal{#1}{0,0}{
\las@length{#2}{las@listlen}
\SQRT{\thelas@listlen}{\@dim}
\FLOOR{\@dim}{\@dim}
\ifnum\intcalcSqr\@dim=\thelas@listlen
\else
\PackageError{LAS}{Not a square matrix. Please specify the dimensions}{}
\fi
\setcounter{las@matrow}{\@dim}
\setcounter{las@matcol}{\@dim}
}{
\foreach\elem[count=\@i] in {#1} {
\ifnum\@i=1
\setcounter{las@matrow}{\elem}
\else
\ifnum\@i=2
\setcounter{las@matcol}{\elem}
\else
\PackageError{LAS}{Too many options given to mat}{}
\fi
\fi
}
}
\las@createMatrix{\thelas@matrow}{\thelas@matcol}{#2}
}
\renewcommand{\vec}[2][col]{ % overrides \vec in amsmath
\ensuremath{
\las@vec{#1}{#2}
\begin{bNiceMatrix}
\las@resMatrix
\end{bNiceMatrix}
\ifstrequal{#1}{row}{^T}{}
}
}
\newcommand{\mat}[2][0,0]{
\ensuremath{
\las@mat{#1}{#2}
\begin{bNiceMatrix}
\las@resMatrix
\end{bNiceMatrix}
}
}
\makeatother
\begin{document}
This works
\begin{equation*}
\mat{1,...,9}
\end{equation*}
This \mat{\frac{1}{3},\frac{2}{4},0,0} also works
Vectors \vec{1,...,4} are fine too
% This doesn't work
% \begin{align*}
% \mat{1,...,9}
% \end{align*}
% This \mat{\sqrt{2},0,0,0} also doesn't work
%
% \vec{\sqrt{2},0,0,0} doesn't work either
\end{document}
上述代码来自我正在制作的一个更大的包,但为了便于重现错误,我将它们合并到同一个文档中。文档目前编译正常,但是取消注释文档中标记为不起作用的任何内容都会导致错误。我还没有弄清楚到底是什么触发了错误,但我从经验中知道在环境中使用\vec
或都不会起作用。在这些宏中的任何一个内使用也不起作用。\mat
align
\sqrt
关于此包有一个相关问题这里。
答案1
问题在于,正如 Don Hosek 所解释的那样\xdef
。
我建议根据expl3
需要\foreach
和否采用不同的循环管理\xdef
。
\documentclass{article}
\usepackage{amsmath}
\ExplSyntaxOn
\NewDocumentCommand{\mat}{ o m }
{
\IfNoValueTF { #1 }
{ \las_mat_compute_order:n { #2 } }
{ \las_mat_shape:nn { #1 } { #2 } }
\las_mat:n { #2 }
}
\RenewDocumentCommand{\vec}{ O{} m }
{
\keys_set:nn { las/vec } { row , #1 }
\int_set:Nn \l_las_mat_cols_int { \clist_count:n { #2 } }
\las_mat:n { #2 }
\bool_if:NT \l_las_mat_colvec_bool { ^T }
}
\keys_define:nn { las/vec }
{
col .bool_set:N = \l_las_mat_colvec_bool,
row .bool_set_inverse:N = \l_las_mat_colvec_bool,
col .default:n = true,
row .default:n = true,
}
\int_new:N \l_las_mat_cols_int
\int_new:N \l_las_mat_rows_int
\seq_new:N \l_las_mat_entries_seq
\cs_new_protected:Nn \las_mat_compute_order:n
{
\fp_compare:nF
{% is the number of items a square?
floor ( sqrt ( \clist_count:n { #1 } ) )
=
sqrt ( \clist_count:n { #1 } )
}
{% no, it isn't
\PackageError{LAS}{Not~a~square~matrix}{Please~specify~the~dimensions}
}
\int_set:Nn \l_las_mat_cols_int { \fp_eval:n { ceil ( sqrt ( \clist_count:n { #1 } ) ) } }
% \int_set:Nn \l_las_mat_rows_int { \l_las_mat_cols_int }
}
\cs_new_protected:Nn \las_mat_shape:nn
{
\int_set:Nn \l_las_mat_rows_int { \clist_item:nn { #1 } { 1 } }
\int_set:Nn \l_las_mat_cols_int { \clist_item:nn { #1 } { 2 } }
\int_compare:nF
{% do we have enough entries?
\l_las_mat_cols_int * \l_las_mat_rows_int = \clist_count:n { #2 }
}
{% no, we don't
\PackageError{LAS}
{
Not~enough~entries~`\clist_item:nn { #1 } { 1 }~*~\clist_item:nn { #1 } { 1 }~
!=~\clist_count:n { #2 }'
}
{
Attempt~to~define~a~\clist_item:nn { #1 } { 1 }~by~\clist_item:nn { #1 } { 2 }~
matrix~with~\clist_count:n { #1 }~elements
}
}
}
\cs_new_protected:Nn \las_mat:n
{
\seq_set_from_clist:Nn \l_las_mat_entries_seq { #1 }
\begin{bmatrix}
\seq_map_indexed_function:NN \l_las_mat_entries_seq \l__mat_entry_add:nn
\end{bmatrix}
}
\cs_new:Nn \l__mat_entry_add:nn
{
#2 % the entry
\int_compare:nTF { \int_mod:nn { #1 } { \l_las_mat_cols_int } = 0 }
{ \\ }
{ & }
}
\ExplSyntaxOff
\begin{document}
\[
\mat{1,0,0,1}\ne
\mat{1,2,3,4,5,6,7,8,9}\ne
\mat[2,3]{1,2,3,4,5,6}\ne
\vec{1,2,3}\ne\vec[col]{1,2,3}
\]
Errors
\[
\mat{1,0,0}\qquad
\mat[3,2]{1,2,3,4,5}
\]
\end{document}
答案2
首先,关于 TeX/LaTeX 调试的提示。LaTeX 喜欢为了方便用户而隐藏错误上下文,但不幸的是,这种做法不利于程序员。在调试宏时,我会添加
\errorcontextlines=5
到您的文件,以便您可以通过宏扩展进行“回溯”来查看问题出在哪里。
您正确地怀疑这是一个脆弱的命令/移动参数问题。由于您\xdef
在这里所做的,您在宏中创建了自己的移动参数。不幸的是,LaTeX 并不神奇,因此原始语言\xdef
不知道如何保护像 这样的强化命令\sqrt
。解决此问题的方法是将您的\xdef
s 替换为\protected@xdef
s。
\las@createMatrix
这是修复问题的命令的修订版本:
\newcommand{\las@createMatrix}[3]{ % dims, rows
\setcounter{las@matrow}{#1}
\setcounter{las@matcol}{#2}
\setcounter{las@listlen}{0}
\las@length{#3}{las@listlen} % get the length of the list after expansion
\setcounter{las@matsize}{\thelas@matrow*\thelas@matcol}
\ifnum\thelas@matsize=\thelas@listlen % if neq
\else
\PackageError{LAS}{Attempt to define a \arabic{las@matrow} by \arabic{las@matcol} matrix with \arabic{las@listlen} elemets}{}
\fi
\xdef\las@resMatrix{}
\foreach \elem[count=\@i] in {#3}{
% \xdef\las@resMatrix{\las@resMatrix \elem \\}
\ifnum\@i=\thelas@matsize
\protected@xdef\las@resMatrix{\las@resMatrix\elem}
\else
\ifnum\intcalcMod{\@i}{\thelas@matcol}=0
\protected@xdef\las@resMatrix{\las@resMatrix \elem \\}
\else
\protected@xdef\las@resMatrix{\las@resMatrix \elem &}
\fi
\fi
}
}
并且,为了完整起见,这里是我提升时改进的输出\errorcontextlines
:
ERROR: Use of \\mat doesn't match its definition.
--- TeX said ---
\@ifnextchar ... \reserved@d =#1\def \reserved@a {
#2}\def \reserved@b {#3}\f...
\las@resMatrix ->\protect \sqrt
{2}&0\\0&
\pgffor@body ...ef \las@resMatrix {\las@resMatrix
\elem } \else \ifnum \intc...
<to be read again>
\pgffor@endhook
\pgffor@invokebody ...pgffor@body \pgffor@endhook
\ifx \pgffor@assign@after@...
\pgffor@next ...=\pgffor@value \pgffor@invokebody
\pgffor@scan
\pgffor@values ->\sqrt {2},0,0,0,
\pgffor@stop ,
\las@createMatrix ...@resMatrix \elem &} \fi \fi }
\las@mat ...{\thelas@matrow }{\thelas@matcol }{#2}
\\mat [#1]#2-> \las@mat {#1}{#2}
\ensuremath { \begin {bNiceMatrix} \[email protected] This \mat{\protect\sqrt{2},0,0,0}
also doesn't work