我目前正在用乳胶写一些东西,需要大量使用形式为
$\begin{bmatrix*}
\lambda_1 & 0 & 0\\
0 & \lambda_2 & 0\\
0 & 0 & \lambda_3\\
\end{bmatrix*}$
具有可变数量的参数\lambda
。
之前,我找到了一个脚本,它可以帮助我创建具有多个参数的向量,从而使我免于为它们重写矩阵:
\newcommand{\vect}[1]{%
\begin{bmatrix} #1\checknextarg}
\newcommand{\checknextarg}{\@ifnextchar\bgroup{\gobblenextarg}{ \end{bmatrix}}}
\newcommand{\gobblenextarg}[1]{ \\ #1\@ifnextchar\bgroup{\gobblenextarg}{ \end{bmatrix}}}
虽然此脚本适用于向量,但我想不出一种通用的方法来为对角矩阵制作类似的东西。找到这样的脚本将为我节省大量时间。我曾尝试使用 lualatex 来实现这一点,但我对它不熟悉,而且这对于尝试拥有可变数量的参数和相应数量的 0 没有帮助。我希望的是,对于上面的例子,我可以简单地$\diagmat{\lambda_1}{\lambda_2}{\lambda_3}$
对任意数量的参数执行类似于的操作,类似于我可以用的操作\vect
。虽然我不确定这是否可能,但任何可以节省我编写这些矩阵的时间和空间的方法都会受到赞赏。
答案1
如果你想使用 LuaLaTeX,有一个有趣的解决方案:
%!TEX program = lualatex
\documentclass{standalone}
\usepackage{amsmath}
\usepackage{mathtools}
\usepackage{luacode}
%More than 10 columns
\setcounter{MaxMatrixCols}{20}
\begin{luacode*}
function diagonal_matrix (n)
local matrix = {}
for i = 1,n do
matrix[i] = {}
for j = 1, n do
if i == j then
matrix[i][j] = [[\lambda_{]]..tostring(i)..[[}]]
else
matrix[i][j] = "0"
end
end
end
tex.print([[\begin{bmatrix*}]])
for i=1,n do
tex.print(table.concat(matrix[i], [[ & ]]))
tex.print([[\\]])
end
tex.print([[\end{bmatrix*}]])
end
\end{luacode*}
\newcommand{\diagmatrix}[1]{\directlua{diagonal_matrix(#1)}}
\usepackage{amsmath}
\begin{document}
$\diagmatrix{15}$
\end{document}
答案2
答案3
这是 的工作expl3
。
\documentclass{article}
\usepackage{amsmath,xparse}
\ExplSyntaxOn
\NewDocumentCommand{\diagonal}{O{b}m}
{% #1 = fences, #2 = entries, comma separated
\egreg_diagonal:nn { #1 } { #2 }
}
\seq_new:N \l__egreg_diagonal_entries_seq
\seq_new:N \l__egreg_diagonal_row_seq
\cs_new_protected:Nn \egreg_diagonal:nn
{
\seq_set_from_clist:Nn \l__egreg_diagonal_entries_seq { #2 }
\begin{#1matrix}
\int_step_function:nN { \seq_count:N \l__egreg_diagonal_entries_seq } \__egreg_diagonal:n
\end{#1matrix}
}
\cs_new_protected:Nn \__egreg_diagonal:n
{% #1 = row number
\seq_clear:N \l__egreg_diagonal_row_seq
\int_step_inline:nn { \seq_count:N \l__egreg_diagonal_entries_seq }
{
\int_compare:nTF { #1 == ##1 }
{
\seq_put_right:Nx \l__egreg_diagonal_row_seq
{
\seq_item:Nn \l__egreg_diagonal_entries_seq { #1 }
}
}
{
\seq_put_right:Nn \l__egreg_diagonal_row_seq { 0 }
}
}
\seq_use:Nn \l__egreg_diagonal_row_seq { & } \\
}
\ExplSyntaxOff
\begin{document}
\[
\diagonal{1} \qquad \diagonal[p]{1,2} \qquad
\diagonal[B]{1,2,3} \qquad \diagonal[v]{1,2,3,4}
\]
\[
\diagonal{\lambda_1,\lambda_2,\lambda_3,\lambda_4,\lambda_5,
\lambda_6,\lambda_7,\lambda_8,\lambda_9}
\]
\end{document}
\int_step_function:nN
在这里,我利用“一次性”提供结果的事实,因此我们可以从第一个单元格开始构建整个矩阵。
可选参数表明栅栏的形状,其样式与括号、圆括号、大括号、垂直线和双垂直线的amsmath
: (默认)相同。b
p
B
v
V
强制参数是用逗号分隔的对角线条目列表。
外循环在每一步构建一行。它调用内循环将条目添加到序列中:0
如果我们不在对角线上,则添加正确的条目,否则添加正确的条目。接下来,序列在&
每个项目之间传递,并\\
结束该行。
警告:无法生成空对角矩阵。可以添加条目列表是否为空的测试:将主函数更改为
\cs_new_protected:Nn \egreg_diagonal:nn
{
\seq_set_from_clist:Nn \l__egreg_diagonal_entries_seq { #2 }
\begin{#1matrix}
\int_compare:nTF { \seq_count:N \l__egreg_diagonal_entries_seq == 0 }
{
{\:}
}
{
\int_step_function:nN { \seq_count:N \l__egreg_diagonal_entries_seq } \__egreg_diagonal:n
}
\end{#1matrix}
}
并且调用\diagonal{}
会产生
一种不同的实现,其中人们可以选择是否输入明确的对角线元素或只是遵循一种模式。
\documentclass{article}
\usepackage{amsmath,xparse}
\ExplSyntaxOn
\NewDocumentCommand{\diagonal}{m}
{
\group_begin:
\keys_set:nn { egreg/diagonal } { #1 }
\egreg_diagonal:
\group_end:
}
\keys_define:nn { egreg/diagonal }
{
type .tl_set:N = \l__egreg_diagonal_type_tl,
type .initial:n = b,
entries .clist_set:N = \l_egreg_diagonal_entries_clist,
pattern .code:n = \cs_set_protected:Nn \__egreg_diagonal_pattern:n { #1 },
items .int_set:N = \l__egreg_diagonal_items_int,
}
\seq_new:N \l__egreg_diagonal_entries_seq
\seq_new:N \l__egreg_diagonal_row_seq
\cs_generate_variant:Nn \seq_set_from_clist:Nn { NV }
\cs_new_protected:Nn \egreg_diagonal:
{
\cs_if_exist:NTF \__egreg_diagonal_pattern:n
{
\int_step_inline:nn { \l__egreg_diagonal_items_int }
{
\seq_put_right:Nn \l__egreg_diagonal_entries_seq { \__egreg_diagonal_pattern:n { ##1 } }
}
}
{
\seq_set_from_clist:NV \l__egreg_diagonal_entries_seq \l_egreg_diagonal_entries_clist
}
\begin{\l__egreg_diagonal_type_tl matrix}
\int_compare:nTF { \seq_count:N \l__egreg_diagonal_entries_seq == 0 }
{
{\:}
}
{
\int_step_function:nN { \seq_count:N \l__egreg_diagonal_entries_seq } \__egreg_diagonal:n
}
\end{\l__egreg_diagonal_type_tl matrix}
}
\cs_new_protected:Nn \__egreg_diagonal:n
{% #1 = row number
\seq_clear:N \l__egreg_diagonal_row_seq
\int_step_inline:nn { \seq_count:N \l__egreg_diagonal_entries_seq }
{
\int_compare:nTF { #1 == ##1 }
{
\seq_put_right:Nx \l__egreg_diagonal_row_seq { \seq_item:Nn \l__egreg_diagonal_entries_seq { #1 } }
}
{
\seq_put_right:Nn \l__egreg_diagonal_row_seq { 0 }
}
}
\seq_use:Nn \l__egreg_diagonal_row_seq { & } \\
}
\ExplSyntaxOff
\begin{document}
\[
\diagonal{entries=} \qquad
\diagonal{entries=1} \qquad \diagonal{type=p,entries={1,2}} \qquad
\diagonal{type=B,entries={1,2,3}} \qquad \diagonal{type=v,entries={1,2,3,4}}
\]
\[
\diagonal{pattern=\lambda_{#1},items=9}
\]
\end{document}
输出与以前相同。
答案4
我可以提供一个\romannumeral
扩展驱动的尾递归循环,它不需要 Lua-/ε-TeX-扩展。
语法与您要求的不同:
您建议:\diagmat{\lambda_1}{\lambda_2}..{\lambda_N}
。
我的循环确实:\diagmat{{\lambda_1}{\lambda_2}..{\lambda_N}}
。
在我的循环中,\diagmat
-macro 处理一个包含以下内容的参数:⟨对角线值列表⟩其长度可以是任意的。
一些伪代码:
\diagmat{⟨list of diagonal values⟩}
→ \romannumeral0\@diagmat{⟨list of diagonal values⟩}% {⟨list of zeros for next row⟩}% initialized empty {⟨list of rows⟩}% initialized empty {⟨new list of rows⟩}% initialized empty {⟨amount of diagonal values⟩}% initialized 0
\@diagmat
是一个\romannumeral
扩展驱动的尾递归循环,它从一次迭代到下一次迭代/从一次对自身的调用到下一次对自身的调用修改/(ex)更改其自身的参数,以便如下工作:
Initialize:
⟨list of diagonal values⟩ := User-provided list of undelimited arguments; ⟨list of zeros for next row⟩ := empty; ⟨list of rows⟩ := empty; ⟨new list of rows⟩ := empty; ⟨amount of diagonal values⟩ := 0;
STEP 1:
If ⟨list of diagonal values⟩ is blank, i.e., is empty or contains only spaces, THEN // -- Comment: The loop for attaching "\\" to the end of each table-row: IF ⟨list of rows⟩ is empty, THEN IF ⟨new list of rows⟩ is empty, THEN Terminate \romannumeral-expansion; ELSE Terminate \romannumeral-expansion and deliver \begingroup \c@MaxMatrixCols=⟨amount of diagonal values⟩\relax $\begin{bmatrix*}⟨new list of rows⟩\end{bmatrix*}$% \endgroup; ENDIF; ELSE ⟨new list of rows⟩ := ⟨new list of rows⟩ + first element of ⟨list of rows⟩ + "\\" ; ⟨list of rows⟩ := result of removing first element from ⟨list of rows⟩ ; GOTO STEP 1; ENDIF; ELSE IF ⟨list of rows⟩ is empty, THEN // -- Comment: Attach another matrix-row ⟨list of rows⟩ := ⟨new list of rows⟩ + "{" IF ⟨list of zeros for next row⟩ is not empty, THEN + ⟨list of zeros for next row⟩ + "&" ENDIF; + first element of ⟨list of diagonal values⟩ + "}"; IF ⟨list of zeros for next row⟩ is empty, THEN ⟨list of zeros for next row⟩ := "0"; ELSE ⟨list of zeros for next row⟩ := ⟨list of zeros for next row⟩ + "&0"; ENDIF; ⟨list of diagonal values⟩ := result of removing first element from ⟨list of diagonal values⟩; ⟨new list of rows⟩ := empty; ⟨amount of diagonal values⟩ := ⟨amount of diagonal values⟩ + 1; ELSE // -- Comment: The loop for attaching "&0" to the end of each matrix-row as there will be another matrix-row: ⟨new list of rows⟩ := ⟨new list of rows⟩ + "{" + first element of ⟨list of rows⟩ + "&0}"; ⟨list of rows⟩ := result of removing first element from ⟨list of rows⟩; ENDIF; GOTO STEP 1; ENDIF;
以下是示例:
\documentclass[a4paper]{article}
\usepackage{amsmath}
\usepackage{mathtools}
%===================[adjust margins/layout for the example]====================
\csname @ifundefined\endcsname{pagewidth}{}{\pagewidth=\paperwidth}%
\csname @ifundefined\endcsname{pdfpagewidth}{}{\pdfpagewidth=\paperwidth}%
\csname @ifundefined\endcsname{pageheight}{}{\pageheight=\paperheight}%
\csname @ifundefined\endcsname{pdfpageheight}{}{\pdfpageheight=\paperheight}%
\textwidth=\paperwidth
\oddsidemargin=1.25cm
\marginparsep=.2\oddsidemargin
\marginparwidth=\oddsidemargin
\advance\marginparwidth-2\marginparsep
\advance\textwidth-2\oddsidemargin
\advance\oddsidemargin-1in
\evensidemargin=\oddsidemargin
\textheight=\paperheight
\topmargin=1.25cm
\footskip=.5\topmargin
{\normalfont\global\advance\footskip.5\ht\strutbox}%
\advance\textheight-2\topmargin
\advance\topmargin-1in
\headheight=0ex
\headsep=0ex
\pagestyle{plain}
\parindent=0ex
\parskip=0ex
\topsep=0ex
\partopsep=0ex
%==================[eof margin-adjustments]====================================
\makeatletter
%%-----------------------------------------------------------------------------
%% Exchange things in the token-stream:
%%.............................................................................
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument is blank (empty or only spaces):
%%-----------------------------------------------------------------------------
%% -- Take advantage of the fact that TeX discards space tokens when
%% "fetching" _un_delimited arguments: --
%% \UD@CheckWhetherBlank{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that
%% argument which is to be checked is blank>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not blank>}%
\newcommand\UD@CheckWhetherBlank[1]{%
\romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#1{}{}}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%% \UD@ExtractFirstArg{ABCDE} yields {A}
%%
%% \UD@ExtractFirstArg{{AB}CDE} yields {{AB}}
%%
%% Due to \romannumeral-expansion the result is delivered after two
%% expansion-steps/after "hitting" \UD@ExtractFirstArg with \expandafter
%% twice.
%%
%% \UD@ExtractFirstArg's argument must not be blank.
%% This case can be cranked out via \UD@CheckWhetherBlank before calling
%% \UD@ExtractFirstArg.
%%
%% Use frozen-\relax as delimiter for speeding things up.
%% I chose frozen-\relax because David Carlisle pointed out in
%% <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
%%
%%.............................................................................
\@ifdefinable\UD@RemoveTillFrozenrelax{%
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}%
{\long\def\UD@RemoveTillFrozenrelax#1#2}{{#1}}%
}%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral\expandafter
\UD@PassFirstToSecond\expandafter{\romannumeral
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}{\UD@stopromannumeral#1}%
}{%
\UD@stopromannumeral\romannumeral\UD@ExtractFirstArgLoop
}%
}{%
\newcommand\UD@ExtractFirstArg[1]%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\expandafter\UD@stopromannumeral\UD@firstoftwo#1{}}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%------------------------------------------------------------------------------
% Expandable incrementing of natural number formed by a sequence of
% explicit catcode-12-character-tokens-from-the-set {0,1,2,3,4,5,6,7,8,9}
%..............................................................................
% \Increment{<natural number k as sequence of explicit catcode-12-character-
% tokens from the set 0123456789>}
% ->
% <natural number (k+1) as sequence of explicit catcode-12-character-tokens
% from the set 0123456789>
% In expansion-contexts the result is delivered after two expansion-steps/is
% obtained by "hitting" \Increment with \expandafter twice.
%------------------------------------------------------------------------------
\newcommand\Increment[1]{%
\romannumeral
\UD@IncrementReverse{\UD@IncrementFork{}}{\relax}{}#1\relax
}%
\newcommand\UD@IncrementReverse[4]{%
\ifx\relax#4%
\expandafter\UD@firstoftwo
\else
\expandafter\UD@secondoftwo
\fi
{#1#3#2}{\UD@IncrementReverse{#1}{#2}{#4#3}}%
}%
\@ifdefinable\UD@IncrementSelect{%
\long\def\UD@IncrementSelect#10123456789\relax#2#3\relax\relax\relax{#2}%
}%
\newcommand\UD@IncrementFork[2]{%
\UD@IncrementSelect
#2123456789\relax{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#11}%
0#223456789\relax{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#12}%
01#23456789\relax{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#13}%
012#2456789\relax{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#14}%
0123#256789\relax{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#15}%
01234#26789\relax{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#16}%
012345#2789\relax{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#17}%
0123456#289\relax{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#18}%
01234567#29\relax{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#19}%
012345678#2\relax{\UD@IncrementFork{#10}}%
0123456789#2{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#11\relax}%
0123456789\relax{\UD@IncrementReverse{\UD@stopromannumeral}{}{}#11#2}%
\relax\relax\relax
}%
%%-----------------------------------------------------------------------------
%% \diagmat which initiates the \romannumeral0-expansion-driven
%% \@diagmat-tail-recursive loop.
%%.............................................................................
\newcommand\diagmat[1]{%
\romannumeral\@diagmat{#1}{}{}{}{0}%
}%
\newcommand\@diagmat[5]{%
% #1 = <List of diagonal values>
% #2 = <List of zeros for next row if present>
% #3 = <List of rows>
% #4 = <New list of rows>
% #5 = <Amount of diagonal values>
\UD@CheckWhetherBlank{#1}{%
\UD@CheckWhetherNull{#3}{%
\UD@CheckWhetherNull{#4}{\UD@stopromannumeral}{%
\UD@stopromannumeral
\begingroup\c@MaxMatrixCols=#5\relax$\begin{bmatrix*}#4\end{bmatrix*}$\endgroup
%\def\tempa{{#5}--#4}\show\tempa
}%
}{%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\expandafter\UD@Exchange
\expandafter{%
\romannumeral
\expandafter\expandafter\expandafter\UD@stopromannumeral
\UD@ExtractFirstArg{#3}\\%
}{#4}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}{%
\@diagmat{#1}{#2}%
}%
}%
{#5}%
}%
}{%
\UD@CheckWhetherNull{#3}{%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\Increment{#5}%
}{%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral
\expandafter\UD@Exchange
\expandafter{%
\romannumeral
\expandafter\expandafter\expandafter\UD@stopromannumeral
\UD@ExtractFirstArg{#1}%
}{%
\UD@CheckWhetherNull{#2}{\UD@stopromannumeral}{\UD@stopromannumeral#2&}%
}%
}{#4}%
}{%
\UD@CheckWhetherNull{#2}{%
\UD@PassFirstToSecond{0}%
}{%
\UD@PassFirstToSecond{#2&0}%
}%
{%
\expandafter\@diagmat\expandafter{\UD@firstoftwo{}#1}%
}%
}{}%
}%
}{%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\expandafter\UD@PassFirstToSecond
\expandafter{%
\romannumeral
\expandafter\expandafter\expandafter\UD@stopromannumeral
\UD@ExtractFirstArg{#3}&0%
}{#4}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}{%
\@diagmat{#1}{#2}%
}%
}%
{#5}%
}%
}%
}%
\makeatother
\begin{document}
\verb|\diagmat{}|:%
\diagmat{}%
\smallskip\hrule\smallskip
\verb|\diagmat{{\lambda_1}}|:
\diagmat{{\lambda_1}}%
\smallskip\hrule\smallskip
\verb|\diagmat{{\lambda_1}{\lambda_2}}|:
\diagmat{{\lambda_1}{\lambda_2}}%
\smallskip\hrule\smallskip
\verb|\diagmat{{\lambda_1}{\lambda_2}{\lambda_3}}|:
\diagmat{{\lambda_1}{\lambda_2}{\lambda_3}}%
\smallskip\hrule\smallskip
\begin{verbatim}
\diagmat{
{\lambda_1} {\lambda_2} {\lambda_3} {\lambda_4}
{\lambda_5} {\lambda_6} {\lambda_7} {\lambda_8}
{\lambda_9} {\lambda_{10}} {\lambda_{11}} {\lambda_{12}}
{\lambda_{13}} {\lambda_{14}} {\lambda_{15}} {\lambda_{16}}
{\lambda_{17}} {\lambda_{18}} {\lambda_{19}} {\lambda_{20}}
}
\end{verbatim}
\diagmat{
{\lambda_1} {\lambda_2} {\lambda_3} {\lambda_4}
{\lambda_5} {\lambda_6} {\lambda_7} {\lambda_8}
{\lambda_9} {\lambda_{10}} {\lambda_{11}} {\lambda_{12}}
{\lambda_{13}} {\lambda_{14}} {\lambda_{15}} {\lambda_{16}}
{\lambda_{17}} {\lambda_{18}} {\lambda_{19}} {\lambda_{20}}
}
\end{document}