获取逗号分隔列表的头、尾和最后一个元素

获取逗号分隔列表的头、尾和最后一个元素

我有一个以逗号分隔的列表,存储在宏中。它不为空,元素之间有逗号,但在开头和结尾没有逗号。我该如何获取:

  1. 第一个元素(又称“头部”)?
  2. 最后一个元素?
  3. 除第一个元素(又称“尾部”)之外的每个元素?

为了澄清起见,我想要命令\head和,例如,\last如果\tail

\johnlist={2,3,5,7,11}

然后

\head\johnlist=2
\last\johnlist=11
\tail\johnlist={3,5,7,11} 

顺便说一句,如果可能的话,我更喜欢基于 TikZ 的解决方案。或者,至少,一个在 TikZ 图片环境中有效的解决方案。

答案1

xstring解决方案

\documentclass{article}
\usepackage{xstring}

\def\johnlist{2,3,5,7,11,13}
\def\splicelist#1{
\StrCount{#1}{,}[\numofelem]
\ifnum\numofelem>0\relax
    \StrBefore[1]{#1}{,}[\myhead]%
    \StrBehind[1]{#1}{,}[\mytail]%
    \StrBehind[\numofelem]{#1}{,}[\mylast]%
\else
    \let\myhead#1%
    \let\mylast#1%
    \def\mytail{N/A}
\fi
}

\begin{document}
\splicelist{\johnlist}
\myhead\par
\mylast\par
\mytail


\splicelist{1}
\myhead\par
\mylast\par
\mytail

\end{document}

在此处输入图片描述

答案2

假设列表包含在宏中,否则您不需要提取明确知道的第一个项目,您可以这样做

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\extractfirst}{mm}
 {
  \tl_set:Nx #1 {\clist_item:Nn #2 { 1 } }
 }
\ExplSyntaxOff

完整示例:

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\extractfirst}{mm}
 {
  \tl_set:Nx #1 {\clist_item:Nn #2 { 1 } }
 }
\ExplSyntaxOff

\begin{document}
  \def\johnlist{2,3,5,7}
  \begin{tikzpicture}
    \extractfirst\x\johnlist
    \node[shape=circle,draw=black] at (\x,0) {\x};
  \end{tikzpicture}
\end{document}

在此处输入图片描述

提取最后一个元素也很容易;只需添加

\NewDocumentCommand{\extractlast}{mm}
 {
  \tl_set:Nx #1 {\clist_item:Nn #2 { -1 } }
 }

为了获得列表的尾部,你可以从列表中弹出第一个元素

\NewDocumentCommand{\extracttail}{mm}
 {
  \clist_set:NV #1 #2
  \clist_pop:NN #1 \l_tmpa_tl
 }

以下是示例:

\documentclass{article}
\usepackage{tikz}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\extractfirst}{mm}
 {
  \tl_set:Nx #1 {\clist_item:Nn #2 { 1 } }
 }
\NewDocumentCommand{\extracttail}{mm}
 {
  \clist_set_eq:NN #1 #2
  \clist_pop:NN #1 \l_tmpa_tl
 }
\NewDocumentCommand{\extractlast}{mm}
 {
  \tl_set:Nx #1 {\clist_item:Nn #2 { -1 } }
 }
\ExplSyntaxOff

\begin{document}
  \def\johnlist{2,3,5,7}
  \begin{tikzpicture}
    \extractfirst\x\johnlist
    \node[shape=circle,draw=black] at (\x,0) {\x};
  \end{tikzpicture}

  \begin{tikzpicture}
    \extracttail\x\johnlist
    \foreach \i in \x {
      \node[shape=circle,draw=black] at (\i,0) {\i};
    }
  \end{tikzpicture}

\end{document}

在此处输入图片描述

答案3

PGFMath 有一个数组解析器可用于此目的。语法是{comma delimited list}[index]。您需要外括号,因此要使用它,\johnlist您需要添加括号。由于坐标是通过 pgfmath 传递的,因此您可以在坐标规范中按原样使用它,在其他用途​​中您需要使用\pgfmathparse(或其变体之一)。

\documentclass{article}
\usepackage{tikz}
\begin{document}
  \def\johnlist{2,3,5,7}
  \begin{tikzpicture}
\node[shape=circle,draw=black] at ({\johnlist}[0],0) {\pgfmathparse{{\johnlist}[0]}\pgfmathresult};
  \end{tikzpicture}   
\end{document}

获取尾部需要计算列表中有多少个条目。幸运的是,PGFMath 也有一个用于此的函数。(手册中似乎没有这个函数,我在查看代码以了解它如何知道数组的末尾时偶然发现了它。)该函数是dim(<array>)。这给出了条目总数,因此由于 PGF 数组是 0 索引的,因此您需要减去 1 才能获得最后一个元素。

完整代码:

\documentclass{article}
%\url{http://tex.stackexchange.com/q/115730/86}
\usepackage{tikz}
\begin{document}
  \def\johnlist{2,3,5,7}
  \begin{tikzpicture}

\node[shape=circle,draw=black] at ({\johnlist}[0],0)
{\pgfmathparse{{\johnlist}[0]}\pgfmathresult};

\node[shape=circle,draw=black] at
({{\johnlist}[dim({\johnlist})-1]},0)
{\pgfmathparse{{\johnlist}[dim({\johnlist})-1]}\pgfmathresult};

  \end{tikzpicture}   
\end{document}

注意坐标中的额外括号,以防止内部()解析器混淆。

稍后添加)看来我误解了“tail”这个词的意思。这大大扩展了 PGF 的数组处理能力,所以我严重地建议跳转到 LaTeX3非常很快。尽管如此,还是有可能得到一个动态的尾巴:

\pgfmathsetmacro\len{dim({\johnlist})-1}
\foreach[evaluate=\x as \x using {{\johnlist}[\x]}] \x in {1,...,\len}
\node[draw=red] at (\x,0) {\x};

这可以更普遍地用于获取某种形式的数组切片。

再次添加

评论中的请求是要求进行适当的切片,以便可以保存数组的切片以供重复使用。这在 PGFMath 中不存在,但只需一点技巧就可以实现。它没有快捷方式符号,并且与数组元素快捷方式符号混合时表现不佳。此外,PGFMath 在内部将列表转换为标记列表,因此如果我们停留在 PGFMath 上下文中,我们需要返回一个标记列表,但如果我们想保存列表以供重复使用,我们需要返回一个逗号分隔的列表。因此,对于这两个上下文,splice有两个函数。csvsplice

\documentclass{article}
%\url{http://tex.stackexchange.com/q/115730/86}
\usepackage{tikz}

\makeatletter
\pgfmathdeclarefunction{slice}{3}{%
  \begingroup%
  \def\pgfmath@csv{}%
  \afterassignment\pgfmath@gobbletilpgfmath@\pgfmath@count=#2\relax\pgfmath@%
  \afterassignment\pgfmath@gobbletilpgfmath@\c@pgfmath@counta=#3\relax\pgfmath@%
  \advance\c@pgfmath@counta by -\pgfmath@count\relax
  \expandafter\pgfmathslice@@#1\pgfmath@stop%
}

\pgfmathdeclarefunction{csvslice}{3}{%
  \begingroup%
  \def\pgfmath@csv{,}%
  \afterassignment\pgfmath@gobbletilpgfmath@\pgfmath@count=#2\relax\pgfmath@%
  \afterassignment\pgfmath@gobbletilpgfmath@\c@pgfmath@counta=#3\relax\pgfmath@%
  \advance\c@pgfmath@counta by -\pgfmath@count\relax
  \expandafter\pgfmathslice@@#1\pgfmath@stop%
}

\def\pgfmathslice@@#1{%
  \def\pgfmath@temp{#1}%
  \advance\pgfmath@count by-1\relax%
  \ifx\pgfmath@temp\pgfmath@token@stop%
  \pgfmath@error{Initial array index out of bounds.}{}%
  \def\pgfmathresult{0}%
  \pgfmath@smuggleone\pgfmathresult\endgroup%
  \let\pgfmath@next=\relax%
  \else%
  \ifnum\pgfmath@count=-1\relax%
  \pgfmath@count=\c@pgfmath@counta
  \def\pgfmathresult{{#1}}%
  \let\pgfmath@next=\pgfmathslice@@@%
  \else%
  \let\pgfmath@next=\pgfmathslice@@%
  \fi%
  \fi%
  \pgfmath@next}

\def\pgfmathslice@@@#1{%
  \def\pgfmath@temp{#1}%
  \advance\pgfmath@count by-1\relax%
  \ifnum\pgfmath@count=-1\relax%
   \ifx\pgfmath@temp\pgfmath@token@stop%
    \pgfmathslice@@@@\pgfmath@stop
    \let\pgfmath@next=\relax%
   \else%
    \let\pgfmath@next=\pgfmathslice@@@@%
   \fi
  \else%
   \ifx\pgfmath@temp\pgfmath@token@stop%
    \pgfmath@error{Final array index out of bounds.}{}%
    \pgfmath@smuggleone\pgfmathresult\endgroup%
    \let\pgfmath@next=\relax%
   \else%
    \expandafter\expandafter\expandafter\def\expandafter\expandafter\expandafter\pgfmathresult\expandafter\expandafter\expandafter{\expandafter\pgfmathresult\pgfmath@csv{#1}}%
    \let\pgfmath@next=\pgfmathslice@@@%
   \fi%
  \fi%
  \pgfmath@next}

\def\pgfmathslice@@@@#1\pgfmath@stop{%
  \expandafter\def\expandafter\pgfmathresult\expandafter{\expandafter{\pgfmathresult}}%
  \pgfmath@smuggleone\pgfmathresult\endgroup%
}

\makeatother
  \def\johnlist{2,3,5,7}

\pgfmathparse{slice({\johnlist},0,2)}
\show\pgfmathresult
\pgfmathparse{slice({\johnlist},1,2)}
\show\pgfmathresult
\pgfmathparse{slice({\johnlist},1,3)}
\show\pgfmathresult
\pgfmathparse{csvslice({\johnlist},1,2)}
\show\pgfmathresult
\let\newlist=\pgfmathresult
\pgfmathparse{\newlist[1]}
\show\pgfmathresult
\pgfmathparse{csvslice({\johnlist},1,3)}
\show\pgfmathresult
\pgfmathparse{array(slice({\johnlist},1,2),1)}
\show\pgfmathresult
\pgfmathparse{array({{3},{5}},1)}
\show\pgfmathresult

\pgfmathparse{csvslice({\johnlist},1,dim({\johnlist})-1)}
\show\pgfmathresult

\begin{document}
  \begin{tikzpicture}

\node[shape=circle,draw=black] at ({\johnlist}[0],0)
{\pgfmathparse{{\johnlist}[0]}\pgfmathresult};

\node[shape=circle,draw=black] at
({{\johnlist}[dim({\johnlist})-1]},0)
{\pgfmathparse{{\johnlist}[dim({\johnlist})-1]}\pgfmathresult};

\foreach[evaluate=\x as \x using {{\johnlist}[\x]}] \x in {1,...,\len}
\node[draw=red] at (\x,0) {\x};

  \end{tikzpicture}   
\end{document}

(我是否提到过我认为您应该为此使用 LaTeX3?)

答案4

本着 John Wickerson 自己的回答的精神(没有额外的包),但支持尾部提取。宏\spliceList{<csv list>}定义宏\head\tail

\documentclass{article}

% \spliceList{1,2,3} --> \def\head{1} \def\tail{3,4}
% \spliceList{7}     --> \def\head{1} \def\tail{}
\def\spliceList#1{\expandafter\spliceListAux #1,\END}

\def\spliceListAux#1,#2\END{%
  \def\head{#1}
  \ifx\END#2\END
    \def\tail{}% tail is empty
  \else
    % remove trailing comma
    \expandafter\spliceListAuxAux #2\END 
  \fi
}
\def\spliceListAuxAux#1,\END{\def\tail{#1}}

\begin{document}
  \def\johnlist{2,3,5,7}
  \spliceList{\johnlist}
  List: \johnlist{} Head: \head{} Tail: \tail{}\par
  \spliceList{1}
  List: 1 Head: \head{} Tail: \tail{}\par

\end{document}

在此处输入图片描述


支持\last,这是一项后期的要求,单靠分隔参数是不可能实现的,但需要某种循环构造。

此版本\spliceList{<csv list>}定义了\head\tail\last。它基于\@forLaTeX 内核提供的循环构造,因此,同样不需要额外的软件包:

\documentclass{article}

\makeatletter

% \spliceList{1,2,3} --> \def\head{1} \def\tail{2,3} \def\last{3}
% \spliceList{7}     --> \def\head{7} \def\tail{7} \def\last{7}
% \spliceList{}      --> \let\head=\relax \let\tail=\relax \let\last=\relax 
\def\spliceList#1{%
  \let\head=\relax
  \let\tail=\relax
  \let\last=\relax
  \@for\tmp:=#1\do{%
    % store \tmp in \last (expand once)
    \edef\last{\expandafter\unexpanded\expandafter{\tmp}}%
    % if \head==\relax store \tmp in \head (expand once)
    \ifx\head\relax 
      \edef\head{\expandafter\unexpanded\expandafter{\tmp}}
    \else
      % if \tail==\relax store \tmp in \tail (expand once) 
      \ifx\tail\relax 
        \edef\tail{\expandafter\unexpanded\expandafter{\tmp}}
      % otherwise append ,\tmp to \tail (expand once)
      \else
        \edef\tail{\expandafter\noexpand\tail,\expandafter\unexpanded\expandafter{\tmp}}
      \fi
    \fi
  }
}

\makeatother

\def\testit#1{\spliceList{#1}List: #1 $\rightarrow$ Head: \head{} Tail: \tail{} Last: \last{}\par }

\begin{document}
  \def\johnlist{2,3,5,7}
  \testit{\johnlist}
  \testit{1,2}
  \testit{1}
  \testit{}
\end{document}

在此处输入图片描述

相关内容