嵌套 SplitList 问题,实现快速扩展

嵌套 SplitList 问题,实现快速扩展

我在想我是否可以编写一个新命令,用于在向量空间中快速插入任意数量的小组向量。以下是我想要的:

$\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

复杂的任务需要较低级别(且更强大)的设施。

这个想法是

  1. 具有全局默认结束和开始索引;
  2. 用分号分割输入并分别处理每个项目;
  3. 如果某个项目没有逗号,则使用默认;
  4. 如果某个项目有一个逗号,则使用第二部分作为结束索引;
  5. 如果一个项目有两个逗号,则使用第二部分和第三部分作为开​​始和结束索引。
\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的回答的行为相同。

相关内容