2

I came across "What is the recommended way to assign a value to a variable and retrieve it for later use?" while trying to figure out how to use PGFKEYS. I'm having trouble, however, figuring out how to make it detect keys that are not set.

MWE:

\documentclass{minimal}
\usepackage{pgfkeys}

\newcommand{\setValue}[1]{\pgfkeys{/variables/#1}}
\newcommand{\getValue}[1]{\pgfkeysvalueof{/variables/#1}}
\newcommand{\declare}[1]{%
 \pgfkeys{
  /variables/#1.is family,
  /variables/#1.unknown/.style = {\pgfkeyscurrentpath/\pgfkeyscurrentname/.initial = ##1}
 }%
}

\declare{}

\newcommand{\leftTailA}[1]{%
  \declare{@mLT/} 
  \setValue{@mLT, Label = #1 } 
  \ifx\getValue{@mLT/Label}\empty
  \else
    $>$\getValue{@mLT/Label}$<$
  \fi
}

\newcommand{\leftTailB}[1]{%
  \ifx#1\empty
  \else
    $>$#1$<$
  \fi
}

\pgfkeys{
  /distMarkup/.is family, /distMarkup,
  default/.style = { leftTailLabel = {} },
  leftTailLabel/.estore in = \myLeftTailLabel,
}

\newcommand\distMarkup[1][]{% Note, don't put a space between the , and the #1, why? I don't know.
  \pgfkeys{/distMarkup, default,#1}
  \ifx\myLeftTailLabel\empty
  \else 
    $>$\myLeftTailLabel$<$
  \fi
}

\begin{document}

A: This has a label: \leftTailA{ label number 1 }

A: This has no label: \leftTailA{}

B: This has a label: \leftTailB{ label number 1 }

B: This has no label: \leftTailB{}

MA: This has a label: \distMarkup[leftTailLabel=label number 1]

MA: This has no label: \distMarkup[]

\end{document}

The output from this code sample is:

A: This has a label: > label number 1<

A: This has no label: ><

B: This has a label: > label number 1 <

B: This has no label:

MA: This has a label: >label number 1<

MA: This has no label:

In particular, I don't understand why "A: This has no label" says "><" rather than nothing.

Extra Credit: What does the ##1 do in the \declare declaration?

  • Also, you say "% Note, don't put a space between the , and the #1, why? I don't know." It absolutely does not matter whether or not you put a space there. – Hood Chatham Jan 9 at 4:18
  • I've posted a separate question/MWE that shows that pdflatex is sensitive to this space. See tex.stackexchange.com/questions/523567/… – Christopher Donham Jan 9 at 13:45
4

The main problem I identify looking at your code is line \ifx\getValue{@mLT/Label}\empty. The behavior of \ifx is to compare the two tokens immediately following \ifx and check if they are the same. In this case those two tokens are \getValue and {. These two tokens are never the same, so the false branch is always taken.

The fix is to use \pgfkeysgetvalue to store the value into a macro and then do the comparison:

\newcommand{\leftTailA}[1]{%
  \declare{@mLT/}%
  \setValue{@mLT, Label = #1 }%
  \pgfkeysgetvalue{@mLT/Label}{\temp}
  \ifx\getValue\temp\empty
  \else
    $>$\getValue{@mLT/Label}$<$%
  \fi
}

Similarly the comparison in \leftTailB is wrong. A fix would be to store the argument of \leftTailB into a macro and then compare that macro to \empty with \ifx as in:

\newcommand{\leftTailB}[1]{%
  \def\temp{#1}%
  \ifx\temp\empty
  \else
    $>$#1$<$%
  \fi
}

If you are doing many comparisons, you could define a command called \ifempty:

\makeatletter
\def\ifempty#1{%
    \def\temp{#1}%
    \ifx\temp\empty
       \expandafter\@firstoftwo
    \else
       \expandafter\@secondoftwo
    \fi
}

\def\ifpgfkeyempty#1{%
    \pgfkeysgetvalue{#1}{\temp}%
    \ifx\temp\empty
       \expandafter\@firstoftwo
    \else
       \ifx\temp\relax
           \expandafter\expandafter\expandafter\@firstoftwo
       \else
           \expandafter\expandafter\expandafter\@secondoftwo
       \fi
    \fi
}
\makeatother

The ##1 in \declare is the argument to the undefined key. You define an undefined key handler that makes any undefined key into a new key and stores the value passed into it. If you said directly:

/variables/.unknown/.style = {\pgfkeyscurrentpath/\pgfkeyscurrentname/.initial = #1}

then \pgfkeys{/variables/some new key=some value} makes some new key into a new variable key and stores some value into it. Inside of a macro, #1 refers to the first argument of the macro. The doubled ## is used to escape #1 so that it refers to the argument of the undefined key rather than to the argument of \declare.

To indicate the relationship between #1 and ##1, consider the code:

 \def\test#1{\def\testinner##1{(#1/##1)}}
 \test{a} % This defines \testinner as #1->(a/#1)
 \testinner{b} % expands to (a/b)

Full code (by the way this is still not good code):

\documentclass{minimal}
\usepackage{etoolbox}
\usepackage{pgfkeys}

\newcommand{\setValue}[1]{\pgfkeys{/variables/#1}}
\newcommand{\getValue}[1]{\pgfkeysvalueof{/variables/#1}}
\newcommand{\declare}[1]{%
 \pgfkeys{
  /variables/#1.is family,
  /variables/#1.unknown/.style = {\pgfkeyscurrentpath/\pgfkeyscurrentname/.initial = ##1}
 }%
}

\declare{}

\makeatletter
\def\ifempty#1{%
    \def\temp{#1}%
    \ifx\temp\empty
       \expandafter\@firstoftwo
    \else
       \expandafter\@secondoftwo
    \fi
}

\def\ifpgfkeyempty#1{%
    \pgfkeysgetvalue{#1}{\temp}%
    \ifx\temp\empty
       \expandafter\@firstoftwo
    \else
       \expandafter\@secondoftwo
    \fi
}
\makeatother

\newcommand{\leftTailA}[1]{%
  \declare{@mLT/}%
  \setValue{@mLT/Label = #1 }%
  \ifpgfkeyempty{/variables/@mLT/Label}{}{%
      $>$\getValue{@mLT/Label}$<$%
  }
}

\newcommand{\leftTailB}[1]{%
  \ifempty{#1}{}{%
    $>$#1$<$%
  }
}

\pgfkeys{
  /distMarkup/.is family, /distMarkup,
  default/.style = { leftTailLabel = {} },
  leftTailLabel/.estore in = \myLeftTailLabel,
}

\newcommand\distMarkup[1][]{%
  \pgfkeys{/distMarkup, default,#1}%
  \ifx\myLeftTailLabel\empty
  \else
    $>$\myLeftTailLabel$<$%
  \fi
}

\begin{document}

A: This has a label: \leftTailA{ label number 1 }

A: This has no label: \leftTailA{}

B: This has a label: \leftTailB{ label number 1 }

B: This has no label: \leftTailB{}

MA: This has a label: \distMarkup[leftTailLabel=label number 1]

MA: This has no label: \distMarkup[]

\end{document} 
| improve this answer | |
  • if the \ifx is not the right comparison to use, then I'm fine with replacing it. The solution you propose has the same issue that the other answer has in that I have to add extra code for each comparison I want to do. Is there a replacement for the \ifx or for how the initialization is done? This would simplify things because I have many such instances I need to address. – Christopher Donham Jan 9 at 4:20
  • @ChristopherDonham We need more information. Can you say what you are actually trying to do? You can easily define a new command called \ifempty. – Hood Chatham Jan 9 at 4:22
  • The MWE is farily representative of the use case. The actual situation is that I am using TIKZ to illustrate hypothesis tests for the class I teach on statistics. For different hypothesis tests, I need to print or not print test statistics, alpha values, problem values, probabilities, means, etc. The way I control whether each is printed is by whether or not the value is defined in calls to a command. There are two such commands, one that passes by location (e.g. a typical latex command) and more recently I've been experimenting with passing by name. Writing an \ifempty would be fine. – Christopher Donham Jan 9 at 4:34
  • I added an \ifempty and a \ifpgfkeyempty. You might want to add handling for the case that the pgf key is undefined to \ifpgfkeyempty. If it's undefined, maybe it should count as empty? – Hood Chatham Jan 9 at 4:36
  • 1
    ##1 is the argument to the unknown key. If you say /declared family/unknown key = value then value goes into ##1. – Hood Chatham Jan 13 at 19:44
3

Something like this?

\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgfkeys}

\newcommand{\setValue}[1]{\pgfkeys{/variables/#1}}
\newcommand{\getValue}[1]{\pgfkeysvalueof{/variables/#1}}
\newcommand{\declare}[1]{%
 \pgfkeys{
  /variables/#1.is family,
  /variables/#1.unknown/.style = {\pgfkeyscurrentpath/\pgfkeyscurrentname/.initial = ##1}
 }%
}

\declare{}

\newcommand{\leftTailA}[1]{%
  \declare{@mLT/}% 
  \setValue{@mLT, Label = #1 }%
  \edef\tmp{\getValue{@mLT/Label}}%
  \ifx\tmp\empty
  \else
    $>$\getValue{@mLT/Label}$<$%
  \fi
}

\newcommand{\leftTailB}[1]{%
  \ifx#1\empty
  \else
    $>$#1$<$%
  \fi
}

\pgfkeys{
  /distMarkup/.is family, /distMarkup,
  default/.style = { leftTailLabel = {} },
  leftTailLabel/.estore in = \myLeftTailLabel,
}

\newcommand\distMarkup[1][]{% Note, don't put a space between the , and the #1, why? I don't know.
  \pgfkeys{/distMarkup, default,#1}%
  \ifx\myLeftTailLabel\empty
  \else 
    $>$\myLeftTailLabel$<$%
  \fi
}

\begin{document}

A: This has a label: \leftTailA{ label number 1 }

A: This has no label: \leftTailA{}

B: This has a label: \leftTailB{ label number 1 }

B: This has no label: \leftTailB{}

MA: This has a label: \distMarkup[leftTailLabel=label number 1]

MA: This has no label: \distMarkup[]

\end{document}

enter image description here

| improve this answer | |
  • That is what I am looking for, but it has the problem that I have to add a separate line for each value that I want to test. My MWE has only one value, but my actual code has many such values. Is there a way to put it into the \ifx statement? I was also wondering if my problem has to do with the ##1 code. – Christopher Donham Jan 9 at 4:07
  • 1
    @ChristopherDonham Yes, the problem is the \ifx. The other answer uses the same method as mine but uses \pgfkeysgetvalue{/variables/@mLT/Label}{\temp}, which also stores the value in a macro for the \ifx comparison. The problem is not the ##1 code. It is a well know fact that \ifx does not always compare the things you wanted it to compare. – user194703 Jan 9 at 4:13
1

Here is a fully expandable solution. \pgfkeysvalueof must be expanded three times until one get the actual value of the key, so we need 2*3+1 \expandafters.

\documentclass[varwidth]{standalone}
\usepackage{pgfkeys}

\makeatletter

\def\pgfkeys@firstoftwo#1#2{#1}
\def\pgfkeys@secondoftwo#1#2{#2}

\def\ifpgfkeysempty#1{%
  \if\relax\detokenize\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{\pgfkeysvalueof{#1}}\relax
    \expandafter\pgfkeys@firstoftwo
  \else
    \expandafter\pgfkeys@secondoftwo
  \fi}

\def\pgfkeysmeaning#1{%
  {\ttfamily #1->|\detokenize\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{\pgfkeysvalueof{#1}}|}}

\makeatother

\pgfkeys{%
  /test1/.initial = {},
  /test2/.initial = \relax,
  /test3/.initial = {Foo},
  /test4/.initial = {\def#1\bar{bar}},
}

\begin{document}
\begin{itemize}
\item \pgfkeysmeaning{/test1}

  \ifpgfkeysempty{/test1}{Key is empty}{Key is not empty}
\item \pgfkeysmeaning{/test2}

  \ifpgfkeysempty{/test2}{Key is empty}{Key is not empty}
\item \pgfkeysmeaning{/test3}

  \ifpgfkeysempty{/test3}{Key is empty}{Key is not empty}
\item \pgfkeysmeaning{/test4}

  \ifpgfkeysempty{/test4}{Key is empty}{Key is not empty}
\end{itemize}
\end{document}

enter image description here

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.