![获取逗号分隔列表的头、尾和最后一个元素](https://linux22.com/image/268981/%E8%8E%B7%E5%8F%96%E9%80%97%E5%8F%B7%E5%88%86%E9%9A%94%E5%88%97%E8%A1%A8%E7%9A%84%E5%A4%B4%E3%80%81%E5%B0%BE%E5%92%8C%E6%9C%80%E5%90%8E%E4%B8%80%E4%B8%AA%E5%85%83%E7%B4%A0.png)
我有一个以逗号分隔的列表,存储在宏中。它不为空,元素之间有逗号,但在开头和结尾没有逗号。我该如何获取:
- 第一个元素(又称“头部”)?
- 最后一个元素?
- 除第一个元素(又称“尾部”)之外的每个元素?
为了澄清起见,我想要命令\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
。它基于\@for
LaTeX 内核提供的循环构造,因此,同样不需要额外的软件包:
\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}