如何更改 TikZ foreach 命令中的项目分隔符

如何更改 TikZ foreach 命令中的项目分隔符

我想使用 循环遍历多个句子foreach loop。现在句子可能包含逗号,逗号用作foreach项目分隔符。因此,我想将项目分隔符更改为句子中未出现的内容,例如|

因此,

\foreach \x in {sentenceA,sentenceB,sentenceC}

我想使用

\foreach \x in {sentenceA|sentenceB|sentenceC}

这可能吗?

答案1

这是一个expl3实现:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xparse,pgffor}
\ExplSyntaxOn

\NewDocumentCommand{\Foreach}{ m O{,} m}
 {
  \__foreach_main:nn { #1 } { #2 } { #3 }
 }

\seq_new:N \l__foreach_arg_seq
\clist_new:N \l__foreach_braced_clist

\cs_new_protected:Npn \__foreach_main:nn #1 #2 #3
 {
  \seq_set_split:Nnn \l__foreach_arg_seq { #2 } { #3 }
  \clist_clear:N \l__foreach_braced_clist
  \seq_map_inline:Nn \l__foreach_arg_seq
   {
    \clist_put_right:Nn \l__foreach_braced_clist { { ##1 } }
   }
  \foreach #1 in~\l__foreach_braced_clist
 }
\ExplSyntaxOff

\begin{document}

\Foreach{\x}[|]{sentenceA|se,nt,en,ce,B| sentenceC }{[\x]}

\Foreach{\x}{sentenceA|se,nt,en,ce,B| sentenceC }{[\x]}

\end{document}

语法与 略有不同\foreach \x in {...},但这不应该是一个大问题。默认分隔符是逗号,可以在变量后的可选参数中指定其他分隔符。

在此处输入图片描述

答案2

逗号在 中是硬编码的pgffor\foreach您可以转换之前的列表并将其存储在宏中。以下示例将条目放在花括号中以保护逗号。然后还保留了首空格和尾空格。在示例中,仅删除了首空格。

\documentclass{article}
\usepackage{pgffor}

\makeatletter
\newcommand{\tocommalist}[3]{%
  \begingroup
    \toks@{}
    \let\@tmp\relax
    \long\def\@tocommalist##1#2##2\@nil{%
      \ifx\@tmp\relax
        % \toks@{{##1}}% without initial trimming
        \toks@\expandafter{\expandafter{\romannumeral-`\x##1}}%
      \else
        % \toks@\expandafter{\the\toks@,{##1}}% without initial trimming
        \toks@\expandafter{%
          \the\expandafter\toks@\expandafter,%
          \expandafter{\romannumeral-`\x##1}%
        }%
      \fi
      \def\@tmp{##2}%
      \ifx\@tmp\@empty
        \expandafter\@gobbletwo
      \fi
      \@tocommalist##2\@nil
    }%
    \@tocommalist#3#2\@nil
  \expandafter\endgroup
  \expandafter\def\expandafter#1\expandafter{\the\toks@}%
}
\makeatother

\tocommalist\mylist{|}{sentenceA|se,nt,en,ce,B| sentenceC }
\foreach \x in \mylist {%
  \typeout{[\meaning\x]}%
}

\begin{document}
\foreach \x in \mylist {%
  [\x]\par
}
\end{document}

结果


另一个使用包的示例etoolbox。它也仅修剪条目的开头并删除空条目:

\documentclass{article}

\usepackage{pgffor}
\usepackage{etoolbox}
\usepackage{etexcmds}
\DeclareListParser*{\forvertlist}{|}

\makeatletter
\newcommand{\vertocomma}[2]{%
  \let#1\@empty
  \forvertlist{\@verttocomma{#1}}{#2}%
}
\newcommand{\@verttocomma}[2]{%
  \edef#1{%
    \ifx#1\@empty
    \else
      \etex@unexpanded\expandafter{#1},%
    \fi
    {\etex@unexpanded{#2}}%
  }%
}
\makeatother

\vertocomma\mylist{sentenceA|| |se,nt,en,ce,B| sentenceC |}
\foreach \x in \mylist {%
  \typeout{[\meaning\x]}%
}

\begin{document}
\foreach \x in \mylist {%
  [\x]\par
}
\end{document}

结果

答案3

当不需要自动列表完成时,期权\indrisloop可以处理任何列表分隔符。\newforeach宏自然可以接受任何类型的列表分隔符,并且可以完成列表完成,但它尚未在 CTAN 上实现。这是一个\indrisloop解决方案。即使列表分隔符处于活动状态,这也应该有效。正如您将在示例结果中注意到的那样,列表元素之间的虚假空格被删除了。

\documentclass{article}
\usepackage{catoptions}

\begin{document}

\def\bombadil#1{%
  \typeout{Doing item: #1}%
  \edef\transformedlist{%
    \ifdefFT\transformedlist{}{\expandcsonce\transformedlist}%
    \unexpanded{\\{#1}}%
  }%
}

% \indrisloop[<separator>]{<list>}{<callback>}

\indrisloop[|]{%
  David Carlisle, item 1 |
  Heiko Oberdiek, item 2 |
  David Salomon, item 3 |
  Frank Mittelbach, item 4 |
  Donald Arseneau; item 5
}\bombadil

\show\transformedlist
\end{document}

{\show}
> \transformedlist=macro:

\\{David Carlisle, item 1}\\{Heiko Oberdiek, item 2}\\{David Salomon, item 3}
\\{Frank Mittelbach, item 4}\\{Donald Arseneau; item 5}.

l.21 \show\transformedlist

DE Knuth 喜欢用作\\列表分隔符。

您可以使用以下命令将任意列表转换为逗号分隔的列表\makeintocommalist

% \makeintocommalist{<original.parser>}{<orig.list.cmd>}{<transformed.list.cmd>}

\def\makeintocommalist#1#2#3{%
  \def#3{}%
  \def\bombadil##1{%
    \edef#3{%
      \expandcsonce#3{\unexpanded{##1}}%
      \iflastindris\else,\fi
    }%
  }%
  \indrisloop*[#1]{#2}\bombadil
}

例子:

\def\alist{%
  David Carlisle, item 1 |
  Heiko Oberdiek, item 2 |
  David Salomon, item 3 |
  Frank Mittelbach, item 4 |
  Donald Arseneau; item 5 |
  Enrico Gregorio/another great |
  Ulrich Diez, yes! |
  Joseph Wright, also/+
}

\makeintocommalist{|}\alist\blist
\show\blist

> \blist=macro:->

{David Carlisle, item 1},{Heiko Oberdiek, item 2},{David Salomon, item 3},
{Frank Mittelbach, item 4},{Donald Arseneau; item 5},{Enrico Gregorio/another great},
{Ulrich Diez, yes!},{Joseph Wright, also/+}.

l.53 \show\blist

编辑

现在我记得期权软件包中有一个\cptforeach命令可以完全满足您的要求。但是它不能自动完成列表。请看下面的操作。它可以处理任意(甚至奇怪的)列表项分隔符。

% \cptforeach[<optional.sep>] <holders> \in <list> \do {<callback>}

% Note the list separators in these loops:

\tikz[shading=ball]
  \cptforeach[:] \x+\cola \in 0+red : 1+green : 2+blue : 3+yellow \do {%
    \cptforeach[|] \y;\colb \in {0;red | 1;green | 2;blue | 3;yellow} \do {%
      \shade[ball color=\cola!50!\colb] (\x,\y) circle (0.4cm);
  }%
};

在此处输入图片描述

以上相当于

\tikz[shading=ball]
  \cptforeach \x/\cola \in 0/red, 1/green, 2/blue, 3/yellow \do {%
    \cptforeach \y/\colb \in {0/red, 1/green, 2/blue, 3/yellow} \do {%
      \shade[ball color=\cola!50!\colb] (\x,\y) circle (0.4cm);
  }%
};

答案4

使用括号 { aaa,nnn!} 分隔项目

\documentclass{article}
\usepackage{tikz}

\begin{document}

\begin{tikzpicture}

\foreach \nn in{{0,1},{1,0},{0,-1},{-1,0}}{
\draw (0,0) -- (\nn);
}
\end{tikzpicture}

\end{document}

在此处输入图片描述

相关内容