错误:宏的使用与其定义不匹配

错误:宏的使用与其定义不匹配

我正在尝试编写一个包来提供一些常见线性代数运算的快捷命令(至少对于我的线性代数入门课程而言)。但是,在某些情况下,我遇到了奇怪的错误。根据其他帖子,我认为我被脆弱与强大的命令问题所困扰(我不完全理解),而且我不确定如何修复它。我尝试将我的宏更改为\DeclareRobustCommand并在各个地方添加\protects,但这些都无法解决问题。具体来说,我遇到了以下错误:

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或都不会起作用。在这些宏中的任何一个内使用也不起作用。\matalign\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。解决此问题的方法是将您的\xdefs 替换为\protected@xdefs。

\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

相关内容