Usando pgfplots, por que obtenho "sequência de controle indefinida" ao tentar usar uma variável foreach como parâmetro para addplot?

Usando pgfplots, por que obtenho "sequência de controle indefinida" ao tentar usar uma variável foreach como parâmetro para addplot?

Como tenho vários gráficos com barras de erro, gostaria de deslocar ligeiramente as barras de erro para tornar o gráfico geral mais legível. No entanto, quando tento fazer isso usando a variável a \foreachcomo a quantidade a ser deslocada, recebo um erro de "sequência de controle indefinida".

\documentclass{article}

\usepackage{tikz}
\usepackage{pgfplots}

\begin{document}

\begin{tikzpicture}
\begin{axis}
\foreach \x/\y in {a/-1cm, b/0cm, c/1cm} {
    \addplot+ [
        %every error bar/.append style={xshift=\y},
        %every node/.style={xshift=\y},
        error bars/y dir=both,
        error bars/y explicit,
    ] coordinates {
        (0, 0) +- (0, 1)
        (1, 0) +- (0, 1)
        (2, 0) +- (0, 1)
    };
}
\end{axis}
\end{tikzpicture}

\end{document}

(O documento não é compilado quando eu descomente um dos dois comentários.)

O que há de errado aqui?

EDITAR: Adicionada outra variável ao arquivo \foreach. No meu caso de uso real, ele conteria o nome do arquivo para plotar os dados ou o rótulo a ser usado para a curva.

Responder1

O problema é uma questão de expansão macro: a \yvariável é avaliada mais tarde - mas naquele momento "mais tarde", o loop foi concluído e \yestá indefinido.

Como já mencionado, \pgfplotsinvokeforeachpossui apenas um argumento de loop (observe que \pgfplotsforeachungroupedsuporta dois argumentos, mas não ajuda aqui).

Aqui está uma abordagem que fazsemprefuncionar, não importa quão complicados sejam seus loops:

\begin{tikzpicture}
\begin{axis}
\foreach \x/\y in {a/-1cm, b/0cm, c/1cm} {
    \edef\temp{
        \noexpand\addplot+ [
            every error bar/.append style={xshift=\y},
            every node/.style={xshift=\y},
            error bars/y dir=both,
            error bars/y explicit,
        ]
        coordinates {
        (0, 0) +- (0, 1)
        (1, 0) +- (0, 1)
        (2, 0) +- (0, 1)
        };
    }
    \temp
}
\end{axis}
\end{tikzpicture}

Introduzi um artificial \edefque cobre todo o corpo do loop. Isso \edefsignifica "definição expandida": define \tempser o resultado totalmente expandido de tudo dentro das chaves. Isso se expandirá \ypara o valor do loop atual. Infelizmente, também irá (tentar) expandir-se \addplot– o que é impossível. Para evitar essa expansão, escrevi a primitiva TeX \noexpandna frente de \addplot.

Finalmente, \tempcontém o corpo do loopsemquaisquer referências a \you \x. Podemos simplesmente executá-lo escrevendo \tempno corpo do loop. Isso faz o trabalho.


Só para os curiosos: você poderia deixar a lista de coordenadas fora de \edef:

\begin{tikzpicture}
\begin{axis}
\foreach \x/\y in {a/-1cm, b/0cm, c/1cm} {
    \edef\temp{
        \noexpand\addplot+ [
            every error bar/.append style={xshift=\y},
            every node/.style={xshift=\y},
            error bars/y dir=both,
            error bars/y explicit,
        ]
    }
    \temp
        coordinates {
        (0, 0) +- (0, 1)
        (1, 0) +- (0, 1)
        (2, 0) +- (0, 1)
    };
}
\end{axis}
\end{tikzpicture}

isso também funciona porque o TeX funciona por meio de expansão - assim que ele "executa" \temp, ele não sabe mais nada sobre o fato de estar "dentro" \tempe simplesmente lerá adiante.

Verhttp://pgfplots.sourceforge.net/TeX-programming-notes.pdfpara detalhes

informação relacionada