Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

What is the recommended way to define a variable inside a document and retrieve its value later?

I've googled several different solutions, usually about ~10 lines long. I was wondering, that there must be a shorter way of doing something like this (Pseudocode):

\setvalue(VARIABLE1){foo foo bar}

and later in the document

$\getvalue(VARIABLE1)$.

I've also seen solutions that include defining a new command, but what if that command is already used inside another package? That doesn't really seem like an independent solution.

share|improve this question
    
As noted in my answer, newcommands can't conflict with an existing command without telling you. LaTeX doesn't let you overwrite with it. –  qubyte Dec 4 '11 at 19:00
1  
Define a macro with \newcommand after loading all packages. Redefine it with \renewcommand in the middle of document. That's what you need I think. Confliction will produce an error when you use \newcommand, don't worry. And the most answers below by now are overkill, especially for a newcomer. –  Leo Liu Dec 5 '11 at 2:30

7 Answers 7

up vote 17 down vote accepted

Well, I know it's not what you want, but the standard route is either with a def or a newcommand. The problem that you've touched on is the global namespace. If you use newcommand it will tell you if the command is already set (as an error in compilation which will name the offending newcommand attempt), protecting you from these issues.

% Set your new variable. In this case it will be 
% called "\MyNewVariable" and the value will be "hello".
\newcommand{\MyNewVariable}{hello}

% Use to get the variable.
\MyNewVariable

If you could prepend them all with your name or some other mnemonic, then you're unlikely to get clashes. This mnemonic then behaves as your own (sort of) namespace.

Assuming that the first instance of your variable is set with a newcommand without raising an error, then a renewcommand can be used to alter that variable later.

share|improve this answer
    
As I said, probably not what you're looking for, but it is minimal. –  qubyte Dec 4 '11 at 18:18

Sounds like you're looking for a key-value system. May I suggest pgfkeys? Including Yiannis' idea of using properties for each variable, I'd do it like this:

\documentclass{article}
\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{}

\begin{document}
 \setvalue{VARIABLE1 = foo foo bar}
 \getvalue{VARIABLE1}

 \declare{test/}
 \setvalue{test/property = 12}
 \getvalue{test/property}
\end{document}

Less than ten lines, even counting the ones that have only braces. The operation is very simple: pgfkeys stores variables as "files" in "directories"; I've decided that yours should be in the /variables directory, so not in the global namespace. (By the way, pgfkeys keys never conflict with normal macro names, so its "global namespace" is different from the macros namespace.)

The \setvalue macro just changes directory appropriately and then calls your assignment. The \getvalue macro retrieves the variable from the correct directory.

The only trick is that in pgfkeys, a key needs to be "known" before it is assigned, or else you have to call it as key/.initial = value. Since I don't want to force you to write that, I created a "handler" for unknown variables that just adds this piece of code behind the scenes.

You declare a variable with properties using \declare{variable/}, and then you can use variable/property as a variable name in \setvalue (you can also use variable/ as a default directory, so write

\setvalue{variable, property 1 = value 1, property 2 = value 2}

which is convenient). The \declare macro just sets up the unknown handler for the "directory" /variables/variable/ (which means that the cryptic line \declare{} at the beginning sets up the /variables/ directory itself).

share|improve this answer
1  
It seems that you have a small typo in your definition of setvalue/getvalue. Your code as is worked for me with \declare{}, but returned error while \setvalue with \declare{abc}. It workes well though when I corrected setvalue to: \newcommand{\setvalue}[1]{\pgfkeys{/variables/#1}}. –  Artem Pelenitsyn Jul 24 at 18:19
1  
@Artem Strange that this went unfixed for so long. Thanks! –  Ryan Reich Jul 24 at 18:22
    
you're welcome to +1 my prev comment :) and thank you too for your solution: I stick to it in my current project! –  Artem Pelenitsyn Jul 24 at 18:59

I would rather define them using a sort of a Lisp way, or object orientated way.

In the minimal below, we use:

\setproperty{test}{aproperty}{12}
\getproperty{test}{aproperty}

Think of them as representing test.aproperty (we actually define them a test@paproperty), this way it is highly unlikely to conflict with any existing commands, other than possibly your own:

The minimal:

\documentclass{article}
\makeatletter
%  Properties a la Lisp.
\def\ece#1#2{\expandafter#1\csname#2\endcsname}%
% \setproperty{ATOM}{PROPNAME}{VALUE} defines the property PROPNAME on the
% ``atom'' ATOM to have VALUE.
% 
\def\setproperty#1#2#3{\ece\protected@edef{#1@p#2}{#3}}%
\def\setpropertyglobal#1#2#3{\ece\protected@xdef{#1@p#2}{#3}}%
%
% 
% \getproperty{ATOM}{PROPNAME} expands to the value of the property
% PROPNAME on ATOM, or to nothing (i.e., \empty), if the property isn't
% present.
% 
\def\getproperty#1#2{%
  \expandafter\ifx\csname#1@p#2\endcsname\relax
  % then \empty
  \else \csname#1@p#2\endcsname
  \fi
}%
%
\makeatother

\begin{document}
\setproperty{test}{aproperty}{12}
\getproperty{test}{aproperty}
\end{document}
share|improve this answer
    
Instead of \edef and \xdef, maybe \protected@edef and \protected@xdef should be used, so as to allow very general values. –  egreg Dec 4 '11 at 23:17

Your code is almost valid ConTeXt: you define a variable using

\setvalue{variable1}{value}

and can get its value using

\getvalue{variable1}

(These are similar to \@namedef and \@nameuse in LaTeX). If you want key-value driven variables, you can use:

\definenamespace
    [VAR]
    [
      name=VAR,
      setup=list,
      command=list,
      parent=VAR,
    ]

\setupVAR
    [a={default A},
     b={default B}]

\defineVAR
    [set1]
    [a={set1 A},
     c={set1 C}]

\defineVAR
    [set2]
    [b={set2 B},
     c={set2 C}]

\starttext
\startlines
  \namedVARparameter{set1}{a} % gives set1 A
  \namedVARparameter{set1}{b} % gives default B
  \namedVARparameter{set1}{c} % gives set1 C
\stoplines
\stoptext
share|improve this answer

How about a token list:

\newtoks\myvar
\myvar={foo bar baz}

and later on in the document when you want to use it:

\the\myvar
share|improve this answer

There are already enough solutions for you. I give the following because your question provides me an opportunity to test a new feature of pathkeys package. Please don't give \NewVariables the name \newvariables. The latter is an internal command in a package. That will be changed soon.

\documentclass{article}
\usepackage{pathkeys}
\makeatletter
\newcommand\setvalues[1]{\pathkeys{citronas/var/set:#1}}
\newcommand\getvalue[1]{\pathkeysval{citronas/var/#1}}
\def\variablelist{}
\newcommand\NewVariables[1]{%
  \cptfor#1\dofor{%
    \def\reserved@a####1=####2=####3\@nil{%
      \cptgaddtolist[;]\variablelist{cmd/####1/####2}%
    }%
    \reserved@a##1==\@nil
  }%
  \cptexpanded{\pathkeys{citronas/var/define*:\expandcsonce\variablelist}}%
}
\makeatother
\NewVariables{%
  var1 = default value1,
  var2 = defaultvalue2
}
\setvalues{var1=new value1, var2=new value2}
\edef\x{\getvalue{var1}}

\begin{document}
\getvalue{var1}
\end{document}

Version 0.0.3 of ltxkeys package (to be uploaded on 2011/12/09) will allow you to do things like the following:

\declarevariables[mynamespace]{%
  var1 = {default value1} = \def\mycolor{#1}\def\cmd##1{##1},
  var2 = default value2, % No callback
  var3 % no default value and no callback
}

\setvarvalues[mynamespace]{var1=new value1, var2=new value2}

\edef\x{\getvarvalue[mynamespace]{var1}}

\begin{document}
\getvarvalue{fam/subfam}{var1}
\end{document}

[mynamespace] will be optional. That means that, to keep \getvarvalue expandable, we need an expandable \@testopt. One can be designed but Joseph Wright, if I got him right the other time, doesn't seem to be comfortable with any expandable \@testopt. My tests have so far revealed nothing worrisome about them. With some luck Joseph will confirm or refute his objection here.

share|improve this answer

A solution with the prop module from LaTeX3:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\setproperty}{ O{standard} m m }
  {
   \cs_if_exist:cF { g_citr_#1_prop } { \prop_new:c { g_citr_#1_prop } }
   \prop_gput:cnn { g_citr_#1_prop } { #2 } { #3 }
  }
\NewDocumentCommand{\getproperty}{ O{standard} m }
  {
   \cs_if_exist:cTF { g_citr_#1_prop } 
     {
      \prop_get:cnN { g_citr_#1_prop } { #2 } \l_citr_value_tl
      \quark_if_no_value:NTF \l_citr_value_tl 
        {
         Inexistent~property~#2
        }
        {
         \tl_use:N \l_citr_value_tl
        }
     }
     { 
      Inexistent~family~#1
     }
  }
\prop_new:N \g_citr_standard_prop
\tl_new:N \l_citr_value_tl
\ExplSyntaxOff

\begin{document}
\setproperty[test]{aproperty}{12}
\getproperty[test]{aproperty}

\getproperty[test]{bproperty}

\setproperty{cproperty}{42}

\getproperty{cproperty}


\end{document}

There is a "standard" property list set; others can be defined and accessed with the optional argument to \setproperty or \getproperty.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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