0

If I read out from a table with \def, say

\pgfplotstablegetelem{\row}{No}\of{\mytable}
\def\No{\pgfplotsretval}

it becomes wrong. By using \xdef

\pgfplotstablegetelem{\row}{No}\of{\mytable}
\xdef\No{\pgfplotsretval}

it becomes correct.

But I had to hear in some comments \xdef is not good, better is \let etc.; so what is the correct way?

enter image description here

\begin{filecontents}[overwrite]{originaltable.csv}
No; Name
3;   aaa
5;   bbb
7;   ccc
\end{filecontents}

\documentclass{article} \usepackage{pgfplotstable} \pgfplotsset{compat=newest} \pgfplotstableread[col sep=semicolon, header=true]{originaltable.csv} {\mytable}

\begin{document}

\section{def-variant -- wrong} \foreach[count=\n from 0] \row in {0,1,2}{%% % In: \pgfplotstablegetelem{\row}{No}\of{\mytable}% \def\No{\pgfplotsretval}% \pgfplotstablegetelem{\row}{Name}\of{\mytable}% \def\Name{\pgfplotsretval}% % Out: \noindent \No, \Name \ }%%

\section{xdef-variant -- correct} \foreach[count=\n from 0] \row in {0,1,2}{%% % In: \pgfplotstablegetelem{\row}{No}\of{\mytable}% \xdef\No{\pgfplotsretval}% \pgfplotstablegetelem{\row}{Name}\of{\mytable}% \xdef\Name{\pgfplotsretval}% % Out: \noindent \No, \Name \ }%% \end{document}

cis
  • 8,073
  • 1
  • 16
  • 45
  • use \xdef only if you know that the argument contains only material that you want and can safely expand. If it fails, like with your Umlauts, it is wrong. – Ulrike Fischer May 29 '21 at 17:37

1 Answers1

1

This isn't really related to anything you want to do with pgfplotstable, it is more a general understanding issue, imho.

What does \def/\gdef do?

\def sets up a macro to expand to the definition you provided, without altering it in any way, so if you \def a macro that contains other macros, and those other macros change some time in the future, the result of your macro will change. (\gdef is just the global variant of \def)

What does \let do?

\let lets a macro to the meaning of the following token. If that following token is a macro itself, \let will set the first macro to have the same meaning as that other macro currently has.

The syntax of \let is somewhat convoluted. If you're using \let\macro<tok> and <tok> is neither a space nor an equals sign, \macro will have the meaning of <tok> (that could be any character with any category code, or a macro, or a primitive). But you can also use an optional = between \macro and <tok>, in which case TeX will only ignore one following space.

What does \edef/\xdef do?

\edef will do the same as \def, but it will fully expand the definition of the macro and assign the result of that expansion to the macro. (\xdef is just the global variant of \edef)

All in an example file

(this uses plain TeX syntax for simplicity, the same would work in LaTeX)

This example will show the different effects of \def, \let, and \edef with an easy to understand example.

% helper functions
\def\exponce#1{\unexpanded\expandafter{#1}}
\def\exptwice#1{\expandafter\exponce\expandafter{#1}}
\def\expthrice#1{\expandafter\exptwice\expandafter{#1}}
\newlinechar`\^^J
\def\showfoursteps#1%
  {%
    \message
      {%
        ^^J%
        \unexpanded{#1}-> \exponce{#1}-> \exptwice{#1}-> \expthrice{#1}
        ^^J%
      }%
  }

\def\BB{\CC} \def\CC{CC}

\def\Adef{\BB} \let\Alet\BB \edef\Aedef{\BB}

\showfoursteps\Adef \showfoursteps\Alet \showfoursteps\Aedef

\def\BB{CC}

\showfoursteps\Adef \showfoursteps\Alet \showfoursteps\Aedef

\bye

This will print the following on the terminal:

\Adef -> \BB -> \CC -> CC

\Alet -> \CC -> CC-> CC

\Aedef -> CC-> CC-> CC

\Adef -> \BB -> CC-> CC

\Alet -> \CC -> CC-> CC

\Aedef -> CC-> CC-> CC

What to watch out for?

In general \def and \let are pretty safe. But \edef can go horribly wrong if used on material that can't be fully expanded safely.

What to use to store the results of something else

If the results of some other routine are stored in a macro and you want to keep those results in another macro, \let is the way to go. If you need to keep the value longer than the current group you can prefix \let with \global: \global\let\foo\bar

If the results are returned by full expansion \edef is the way to go (this is true for many of the functions in expl3 which use \unexpanded at the end to yield some results without expanding them any further, e.g., you'd use \edef\mymacro{\stripspaces{<stuff>}} with \stripspaces being an expandable macro that removes spaces from either end of <stuff>).

If you know exactly how many expansion steps something takes (e.g., \pgfplotsretval needs exactly one step) you can use the \unexpanded primitive and \expandafter to trigger exactly as many expansion steps as you need and that even inside an \edef. So you could as well use the following:

\def\BB{\CC}
\edef\AA{BB is \unexpanded\expandafter{\BB}}

Applied to your situation:

You can use \let to assign \No the current meaning of \pgfplotsretval like so:

\begin{filecontents}[overwrite]{originaltable.csv}
No; Name
3;   aaa
5;   bbb
7;   ccc
\end{filecontents}

\documentclass{article} \usepackage{pgfplotstable} \pgfplotsset{compat=newest} \pgfplotstableread[col sep=semicolon, header=true]{originaltable.csv} {\mytable}

\begin{document}

\section{let-variant -- correct} \foreach[count=\n from 0] \row in {0,1,2}{%% % In: \pgfplotstablegetelem{\row}{No}\of{\mytable}% \let\No\pgfplotsretval% \pgfplotstablegetelem{\row}{Name}\of{\mytable}% \let\Name\pgfplotsretval% % Out: \noindent \No, \Name \ }%% \end{document}

Skillmon
  • 60,462
  • Ok, that's well explained and implemented professionally (with the helper functions).

    But I would say: if I replace def or xdef in my MWE with let, it no longer works.

    And as long as it works with xdef and doesn't issue any warnings ...

    – cis May 29 '21 at 17:50
  • @cis for me it works, I'll post code. – Skillmon May 29 '21 at 17:54
  • Ahh, I had to much {braces}.... – cis May 29 '21 at 17:57
  • @cis that's why I said it'll let it to the meaning of the next token (the opening brace is a token). There is are exceptions to this rule though: \let will ignore the following spaces if you don't use = (and then it'll ignore the first following space), and \let will ignore the first following =. – Skillmon May 29 '21 at 17:59
  • Aha, ok. \let\Name\pgfplotsretval is working; but for me there is the same thing again, if I now get the idea to expand instead of \Name, {The Name is \pgfplotsretval}, all I can do is use \xdef again \xdef\MyName{The Name is \pgfplotsretval} – cis May 29 '21 at 18:03
  • @cis yes, but you could use a trick there (and you should use \edef instead of \xdef). – Skillmon May 29 '21 at 18:04
  • I'm not sure here, but the problem with edef was that \Name only does it in the loop (which should be enough in that special MWE). But if \Name is to be called somewhere in the document,(in- and outside the loop), it only works for me with xdef. – cis May 29 '21 at 18:08
  • @cis yes, because it is global. You can also \let globally using \global\let\Name\pgfplotsretval. But you should only use global assignments if you really need them (and stick to only global assignments for a single macro). – Skillmon May 29 '21 at 18:13