我在想我是否可以编写一个新命令,用于在向量空间中快速插入任意数量的小组向量。以下是我想要的:
$\basis{x; u; v}$
将自动解析为:
$x_1, \sim x_n ; u_1, \sim u_n ; v_1, \sim v_n$
但是如果我想改变任何开始和结束下标,我需要使用这样的语法命令:
$\basis{ {x,2,k}; {u, 3, n-1}; {v, 1, r} }$
解决为:
$x_{2}, \sim x_{k} ; u_3, \sim u_{n-1} ; v_1, \sim v_{r}$
甚至更加灵活,例如:
$\basis{x; {u, 2, k }; v}$
x
并且和的脚本v
保持默认值(1 到 n)。
我目前拥有的是:
\NewDocumentCommand{\basisPrivate}{ m O{1} O{n} } {
{#1}_{#2} \sim {#1}_{#3}
}
\NewDocumentCommand{\basis} { >{\SplitList{;}}m } {
{ \ProcessList{#1}{\basisPrivate} }
}
这只适用于最简单的情况:
$\basis{m; n; k}$
但我不知道如何实现第二个。
有人能给我一些建议,告诉我这是否可行或如何做到这一点吗?任何帮助都将不胜感激!
更新:
感谢@wipet、@egreg、@Mico 提供的宝贵解决方案并抽出时间回答我的问题。
我没有想到在这里可以获得如此强大且通用的模板!
@wipet 的解决方案秉承了简单的哲学;
@egreg 提供了一个根据我的需要量身定制的完整小型库!
@Mico 的模板也很通用,不过我对lua
的语法不是很熟悉,所以只能等以后测试了。
我宁愿不在这里将任何人标记为“最佳”解决方案,你们在这里展示的都是纯金。再次感谢!
答案1
我们可以直接使用 TeX 基元来做什么:
\def\basis #1{\basisA#1;}
\def\basisA #1;#2;#3;{\basisB#1;;\basisB#2;;\basisB#3;}
\def\basisB #1#2;{\iscomma#1#2,\iffalse \basisC#1#2,1,n;\else \basisC#1#2;\fi}
\def\basisC #1,#2,#3;{#1_{#2}, \sim #1_{#3}}
\def\iscomma #1,#2\iffalse{\ifx"#2"}
%% tests:
$\basis{x; u; v}$
$\basis{ {x,2,k}; {u, 3, n-1}; {v, 1, r} }$
$\basis{x; {u, 2, k }; v}$
\bye
答案2
复杂的任务需要较低级别(且更强大)的设施。
这个想法是
- 具有全局默认结束和开始索引;
- 用分号分割输入并分别处理每个项目;
- 如果某个项目没有逗号,则使用默认;
- 如果某个项目有一个逗号,则使用第二部分作为结束索引;
- 如果一个项目有两个逗号,则使用第二部分和第三部分作为开始和结束索引。
\documentclass{article}
\usepackage{amsmath}
\ExplSyntaxOn
\NewDocumentCommand{\basis}{O{n}mO{1}}
{% #1 = default end index
% #2 = items
% #3 = default start index
\zhuoqun_basis:nnn { #2 } { #3 } { #1 }
}
\seq_new:N \l__zhuoqun_basis_groups_seq
\seq_new:N \l__zhuoqun_basis_group_seq
\tl_new:N \l__zhuoqun_basis_groups_first_seq
\cs_new_protected:Nn \zhuoqun_basis:nnn
{
\seq_set_split:Nnn \l__zhuoqun_basis_groups_seq { ; } { #1 }
\seq_pop_left:NN \l__zhuoqun_basis_groups_seq \l__zhuoqun_basis_groups_first_seq
\__zhuoqun_basis_item:Vnn \l__zhuoqun_basis_groups_first_seq { #2 } { #3 }
\seq_map_inline:Nn \l__zhuoqun_basis_groups_seq
{
;\mspace{\thickmuskip}
\__zhuoqun_basis_item:nnn { ##1 } { #2 } { #3 }
}
}
\cs_new_protected:Nn \__zhuoqun_basis_item:nnn
{
\seq_set_split:Nnn \l__zhuoqun_basis_group_seq { , } { #1 }
\int_case:nn { \seq_count:N \l__zhuoqun_basis_group_seq }
{
{1}{\__zhuoqun_basis_make:eee
{ \seq_item:Nn \l__zhuoqun_basis_group_seq { 1 } }
{ #2 }
{ #3 }
}
{2}{\__zhuoqun_basis_make:eee
{ \seq_item:Nn \l__zhuoqun_basis_group_seq { 1 } }
{ #2 }
{ \seq_item:Nn \l__zhuoqun_basis_group_seq { 2 } }
}
{3}{\__zhuoqun_basis_make:eee
{ \seq_item:Nn \l__zhuoqun_basis_group_seq { 1 } }
{ \seq_item:Nn \l__zhuoqun_basis_group_seq { 2 } }
{ \seq_item:Nn \l__zhuoqun_basis_group_seq { 3 } }
}
}
}
\cs_generate_variant:Nn \__zhuoqun_basis_item:nnn { V }
\cs_new_protected:Nn \__zhuoqun_basis_make:nnn
{
#1\sb{#2},\dots,#1\sb{#3}
}
\cs_generate_variant:Nn \__zhuoqun_basis_make:nnn { eee }
\ExplSyntaxOff
\begin{document}
\begin{gather}
\basis{x} \\
\basis{x}[2] \\
\basis[m]{x}[2] \\
\basis{x; u; v} \\
\basis{x; u; v}[2] \\
\basis[m]{x; u; v}[2] \\
\basis{x,2,k; u, 3, n-1; v,r}
\end{gather}
\end{document}
我使用\dots
那比更习惯\sim
。
答案3
为了确保万无一失,我们采用了基于 LuaLaTeX 的解决方案。
观察输入可能非常通用。
它们不必是单字符实体;相反,它们可以是诸如
t_0
、\kappa
和 之类的东西\beta^*
。输入不需要是 ascii 编码的;实际上,它们可以是通用的 utf8 编码字形。(如果 utf8 编码字形出现在文本字体中,但在流行的数学字体中没有,则需要将其装入包装器中
\mbox
。)如果你希望使用
,\sim
而不是,\dots,
作为连接器,只需更改线路tex.sprint (j1.."_{"..j2.."},\\dots,"..j1.."_{"..j3.."}")
到
tex.sprint (j1.."_{"..j2.."},\\sim"..j1.."_{"..j3.."}")
% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{luacode} % for 'luacode' env. and '\luastringN' macro
\begin{luacode}
function basis ( s )
-- split the input string into constituent components
args = {}
for zz in unicode.utf8.gmatch ( s , "[^%;]+" ) do
table.insert ( args , zz )
end
-- process each component in turn
for i=1,#args do
ii = args[i] -- select i'th row of the table
_, c = unicode.utf8.gsub ( ii , "%," , "" ) -- count instances of ','
if c==0 then
j1=ii; j2="1"; j3="n"
else
iii={} -- initialize a scratch table
for zz in unicode.utf8.gmatch ( ii , "[^%,]+" ) do
table.insert ( iii , zz )
end
j1=iii[1];
if c==1 then
j2 = "1"; j3 = iii[2]
else
j2 = iii[2]; j3 = iii[3]
end
end
-- print the result
tex.sprint (j1.."_{"..j2.."},\\dots,"..j1.."_{"..j3.."}")
if i<#args then
tex.sprint ( ";" ) -- separator between tuples
end
end
end
\end{luacode}
%% LaTeX-side code: Define a utility macro.
%% Note: We take care not to expand its argument.
\newcommand\basis[1]{\directlua{basis(\luastringN{#1})}}
\begin{document}
$\basis{y ; z, t_0, t_n; \beta^*,\kappa}$.
\end{document}
答案4
为了好玩,使用 LuaLaTeX 的 LPEG 来解决。
% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{luacode}
\begin{luacode}
local function one_element(x,i,j)
i , j = j and i or j or "1" , j or i or "n"
return x .. "_{" .. i .. "} , \\dots , " .. x .. "_{" .. j .. "}"
end
lpeg.locale(lpeg)
local P , S , C , V = lpeg.P , lpeg.S , lpeg.C, lpeg.V
local MyLPEG =
P { "E" ,
E = V "F" * ( C ( P ";" ) * V "F" ) ^ 0 ,
F = ( V "G" * ( P "," * V "G" ) ^ -2 ) / one_element ,
G = C ( ( 1 - S ",;" ) ^ 0 ) }
function basis(s) return tex.sprint(MyLPEG : match(s)) end
\end{luacode}
\NewDocumentCommand{\basis}{m}{\directlua{basis(\luastringN{#1})}}
\begin{document}
$\basis{y ; z, t_0, t_n; \beta^*, \kappa }$
\end{document}
该行为与Mico的回答的行为相同。