Для людей, у которых нет\regex_replace_all:nnN

Для людей, у которых нет\regex_replace_all:nnN

У меня есть несколько таблиц, которые я хочу прочитать и построить, каждая из них в своем стиле. Для удобства я люблю использовать те же теги. Вот MWE:

\documentclass{minimal}

\usepackage{pgfplotstable}

\begin{filecontents}{tabA.dat}
x   y
0   0
1   1
2   2
3   3
\end{filecontents}
\begin{filecontents}{tabB.dat}
x   y
0   1
1   2
2   3
3   4
\end{filecontents}

\pgfplotsset{compat=newest,
tabA/.style={color=red,mark=*},
tabB/.style={color=black,mark=o},
}

\pgfplotstableread{tabA.dat}\tabA
\pgfplotstableread{tabB.dat}\tabB

\begin{document}
    \begin{tikzpicture}
        \begin{axis}[legend,legend pos=south east]
            \addplot[tabA] table[x=x,y=y] {\tabA};\label{pgf:tabA}\addlegendentry{tabA}
            \addplot[tabB] table[x=x,y=y] {\tabB};\label{pgf:tabB}\addlegendentry{tabB}
        \end{axis}
    \end{tikzpicture}

\end{document}

Могу ли я добиться того же результата, используя \pgfplotsforeachinvokeили \foreach? Что-то вроде

\begin{tikzpicture}
    \begin{axis}[legend,legend pos=south east]
        \pgfplotsinvokeforeach{tabA,tabB}{%
            % The following doesn't work, of course
            \addplot[#1] table[x=x,y=y] {\#1}; % <- Magic goes here 
        }
    \end{axis}
\end{tikzpicture}

Конечно, в этом простом случае я мог бы просто использовать

\addplot[#1] table[x=x,y=y] {#1.dat};

но иногда имя файла не соответствует шаблону, а иногда мне просто нужно прочитать и сохранить таблицу, чтобы изменить ее или использовать повторно несколько раз.

решение1

Добро пожаловать в TeX.SE! Всегда возможно одно:

  1. написание макроса, который собирает список токенов нужным вам образом (в данном случае объединяет ряд \addplotкоманд с соответствующими параметрами);

  2. затем используется второй макрос (в моем коде, созданный из второго аргумента \foreachTable), который выводит что-то вроде \begin{axis}[...]#1\end{axis}, с #1заменой на ранее собранный список токенов, содержащий все \addplotкоманды.

Эта техникавсегда работает(после раскрытия второго макроса входной поток TeX находится в том же состоянии, как если бы вы ввели весь код вручную). Поэтому вы можете использовать его для программного создания таблиц, изображений и всего, что захотите.

Полный код:

\begin{filecontents}{tabA.dat}
x   y
0   0
1   1
2   2
3   3
\end{filecontents}

\begin{filecontents}{tabB.dat}
x   y
0   1
1   2
2   3
3   4
\end{filecontents}

\documentclass[tikz, border=2mm]{standalone}
\usepackage{xparse}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}

\ExplSyntaxOn
\seq_new:N \l__millo_plot_cmds_tl

\cs_new_protected:Npn \millo_foreach_table_do_axis:nNn #1#2#3
  {
    \tl_clear:N \l__millo_plot_cmds_tl
    \clist_map_inline:nn {#1}
      {
        \tl_set:Nn \l_tmpa_tl {#3}
        \regex_replace_all:nnN { \c{myTable} } { \c{##1} } \l_tmpa_tl
        \tl_put_right:NV \l__millo_plot_cmds_tl \l_tmpa_tl
      }

    \exp_args:No #2 \l__millo_plot_cmds_tl
  }

\NewDocumentCommand \foreachTable { m m m }
  {
    \cs_set_protected:Npn \__millo_axis_func:n ##1 {#2}
    \millo_foreach_table_do_axis:nNn {#1} \__millo_axis_func:n {#3}
  }
\ExplSyntaxOff

\pgfplotsset{
  tabA/.style={color=red,mark=*},
  tabB/.style={color=black,mark=o},
}

\pgfplotstableread{tabA.dat}\tabA
\pgfplotstableread{tabB.dat}\tabB

\begin{document}

\begin{tikzpicture}
  \foreachTable{tabA, tabB}
    {
      \begin{axis}[legend, legend pos=south east]
        #1
      \end{axis}
    }
    { \addplot[#1] table[x=x,y=y] {\myTable}; \addlegendentry{#1} }
\end{tikzpicture}

\end{document}

Скриншот

Объяснение вызова:

\foreachTable{tabA, tabB}
  {
    \begin{axis}[legend, legend pos=south east]
      #1
    \end{axis}
  }
  { \addplot[#1] table[x=x,y=y] {\myTable}; \addlegendentry{#1} }

Первый аргумент — список записей (каждая запись приводит к одной \addplotкоманде).

Второй аргумент — это то, что будет вставлено после того, как #1внутри него будут заменены автоматически сгенерированные \addplotкоманды.

Третий аргумент указывает код для каждого автоматически сгенерированного графика с некоторыми удобными заменами:

  • #1заменяется на имя записи (здесь: tabAthen tabB);

  • \myTableзаменен токеном управляющей последовательности, созданным из имени записи (здесь: \tabAдля первой записи, \tabBдля второй).

Если вы хотите вручную добавить больше графиков (в данном случае один до и один после автоматически сгенерированных), вы можете, например, сделать следующее:

\foreachTable{tabA, tabB}
  {
    \begin{axis}[legend, legend pos=south east]
      \addplot {sin(deg(\x))}; \addlegendentry{$\sin$}
      #1
      \addplot {sqrt(\x)};     \addlegendentry{$x\mapsto \sqrt{x}$}
    \end{axis}
  }
  { \addplot[#1] table[x=x,y=y] {\myTable}; \addlegendentry{#1} }

введите описание изображения здесь

Для людей, у которых нет\regex_replace_all:nnN

Если вы l3kernelслишком стары, чтобы иметь \regex_replace_all:nnN, вы можете:

  • добавить \cs_generate_variant:Nn \tl_replace_all:Nnn { Nno }перед \cs_new_protected:Npn \millo_foreach_table_do_axis:nNn #1#2#3;

  • заменить строку

    \regex_replace_all:nnN { \c{myTable} } { \c{##1} } \l_tmpa_tl
    

    с

    \exp_args:NNno
    \tl_replace_all:Nno \l_tmpa_tl { \myTable } { \use:c {##1} }
    

Тогда это должно сработать.при условиичто вы не используете \myTableвнутри скобок. Например, используйте

\foreachTable{tabA, tabB}
  {
    ...
  }
  { \addplot[#1] table[x=x,y=y] \myTable; \addlegendentry{#1} }

вместо:

\foreachTable{tabA, tabB}
  {
    ...
  }
  { \addplot[#1] table[x=x,y=y] {\myTable}; \addlegendentry{#1} }

Связанный контент