3

Say I have 100 nodes that I want to draw with slight varations 100 times. I don't want to copy/paste all the code each time, that would be a lot of duplication and a lot of boilerplate. So instead I am considering making a compact data structure to hold the values.

The question is, what is a Latex-y way of accomplishing this without parsing. That is, how can this be accomplished without writing a custom parser for a custom data structure.

An example data structure that would require parsing would be something like:

\mynodes{1:red,2:blue,3:yellow,....}

That would give you a comma-separated list of id:color, which you could then parse.

I'm wondering though, if there is a way to do this without parsing. So something like (say there exist 100 properties 1-100):

\mynodes{1=red,2=yellow}

But this example is too simple, there could be several values per id. So more like:

\mynodes{1=\keys{color=red,size=1cm},2=\keys{color=yellow,size=1cm},...}

This could be shortened if you had a lookup table for the properties and values, such as:

\mynodes{1=\v{c=r,s=1},2=\v{c=y,s=1},...}

That would then give you an etoolbox list objects to iterate over. Wondering if something like that is possible, and shining some light on how one might go about accomplishing this.

The end result would be a more compact data structure without using any parsing.

1

I suggest an expl3 approach. We store the node data in a property list; for node 1, the property 1 will contain the whole set of data; also we add the properties 1color and 1size for the specific data.

Data can be retrieved with \getmynode.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\mynodes}{m}
 {
  \prop_set_from_keyval:Nn \l_pollard_nodes_prop { #1 }
  \prop_map_inline:Nn \l_pollard_nodes_prop { \pollard_add_property:nn { ##1 } { ##2 } }
 }
\NewExpandableDocumentCommand{\getmynode}{mm}
 {% #1 = node number, #2 = property
  \prop_item:Nn \l_pollard_nodes_prop { #1#2 }
 }

\keys_define:nn { pollard/nodes }
 {
  color .code:n = \prop_put:Nxn \l_pollard_nodes_prop { \l__pollard_nodes_temp_tl color } { #1 },
  size  .code:n = \prop_put:Nxn \l_pollard_nodes_prop { \l__pollard_nodes_temp_tl size  } { #1 },
 }

\prop_new:N \l_pollard_nodes_prop
\cs_generate_variant:Nn \prop_put:Nnn { Nx }

\cs_new_protected:Nn \pollard_add_property:nn
 {
  \tl_set:Nn \l__pollard_nodes_temp_tl { #1 }
  \keys_set:nn { pollard/nodes } { #2 }
 }

\ExplSyntaxOff

\begin{document}

\mynodes{1={color=red,size=1cm},2={color=yellow,size=2cm},}

Node 1 -- color: \getmynode{1}{color}, size: \getmynode{1}{size}

Node 2 -- color: \getmynode{2}{color}, size: \getmynode{2}{size}

\end{document}

enter image description here

With shorthands:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\mynodes}{m}
 {
  \prop_set_from_keyval:Nn \l_pollard_nodes_prop { #1 }
  \prop_map_inline:Nn \l_pollard_nodes_prop { \pollard_add_property:nn { ##1 } { ##2 } }
 }
\NewExpandableDocumentCommand{\getmynode}{mm}
 {% #1 = node number, #2 = property
  \prop_item:Nn \l_pollard_nodes_prop { #1#2 }
 }

\keys_define:nn { pollard/nodes }
 {
  color .code:n = \prop_put:Nxn \l_pollard_nodes_prop { \l__pollard_nodes_temp_tl color } { #1 },
  size  .code:n = \prop_put:Nxn \l_pollard_nodes_prop { \l__pollard_nodes_temp_tl size  } { #1 },
  c     .code:n = \prop_put:Nxx \l_pollard_nodes_prop
                   { \l__pollard_nodes_temp_tl color }
                   { \__pollard_nodes_color:n { #1 } },
  s     .code:n = \keys_set:nn { pollard/nodes } { size=#1cm }
 }

\prop_new:N \l_pollard_nodes_prop
\cs_generate_variant:Nn \prop_put:Nnn { Nxn, Nxx }

\cs_new_protected:Nn \pollard_add_property:nn
 {
  \tl_set:Nn \l__pollard_nodes_temp_tl { #1 }
  \keys_set:nn { pollard/nodes } { #2 }
 }

\cs_new:Nn \__pollard_nodes_color:n
 {
  \str_case:nn { #1 }
   {
    {r}{red}
    {y}{yellow}
    % define the others you need
   }
 }

\ExplSyntaxOff

\begin{document}

\mynodes{1={color=red,size=1cm},2={color=yellow,size=2cm},}

Node 1 -- color: \getmynode{1}{color}, size: \getmynode{1}{size}

Node 2 -- color: \getmynode{2}{color}, size: \getmynode{2}{size}

\mynodes{3={c=r,s=1}}

Node 3 -- color: \getmynode{3}{color}, size: \getmynode{3}{size}

\end{document}

enter image description here

  • I didn't realize you could use underscores in command names (correct me if I'm wrong) woohoo! e.g. cs_generate_variant:Nn – Lance Pollard Mar 23 '18 at 15:08
  • 1
    @LancePollard That's the expl3 convention, holding only between \ExplSyntaxOn and \ExplSyntaxOff – egreg Mar 25 '18 at 20:54
1

I you want to avoid parsing as much as possible and all the values have the same list of properties, you can let TeX parse your input by using a list of macro calls as argument:

\documentclass{article}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\newcommand\mynodes[1]{%
  \begingroup
    \renewcommand\do[3]{%
      Node ##1 has color ``##2'' and size ``##3''.\\%
    }%
    #1%
  \endgroup
}
\begin{document}
\mynodes{\do{1}{red}{1cm}\do{2}{yellow}{1cm}}
\end{document}

enter image description here

1

I am not exactly sure to what level you need the \keys parsed, but I am sure that the listofitems package can parse it for you.

\documentclass{article}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage{listofitems}
\newcommand\mynodes[1]{%
  \setsepchar{,/=}
  \readlist\mynodelist{#1}%
  \foreachitem\x\in\mynodelist{%
    node \mynodelist[\xcnt,1] is as follows:
     \detokenize\expandafter\expandafter\expandafter{\mynodelist[\xcnt,2]}%
    \par%
  }%
}
\begin{document}
\mynodes{1=\keys{color=red,size=1cm},2=\keys{color=yellow,size=1cm}}
\end{document}

enter image description here

For example, to further parse the keys:

\documentclass{article}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage{listofitems}
\newcommand\mynodes[1]{%
  \setsepchar{,/=}
  \readlist\mynodelist{#1}%
  \foreachitem\x\in\mynodelist{%
    node \mynodelist[\xcnt,1] is as follows:\\
     \expandafter\expandafter\expandafter\parsekeys\mynodelist[\xcnt,2]%
     \readlist\keylist{\tmp}%
     \foreachitem\y\in\keylist{The key ``\keylist[\ycnt,1]'' 
        has a value of ``\keylist[\ycnt,2]''.\\}%
    \par%
  }%
}
\def\parsekeys\keys#1{\gdef\tmp{#1}}
\begin{document}
\mynodes{1=\keys{color=red,size=1cm},2=\keys{color=yellow,size=1cm}}
\end{document}

enter image description here

If you avoided bracing the actual \keys in the node list, then listofitems could do it even easier in one fell swoop, rather than the nested \readlist as shown in my 2nd code block.

So the following code also produces the above result, using the input syntax of \mynodes{1:color=red,size=1cm;2:color=yellow,size=1cm}:

\documentclass{article}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage{listofitems}
\newcommand\mynodes[1]{%
  \setsepchar{;/:/,/=}
  \readlist\mynodelist{#1}%
  \foreachitem\x\in\mynodelist{%
    node \mynodelist[\xcnt,1] is as follows:\\
     \foreachitem\y\in\mynodelist[\xcnt]{The key 
        ``\mynodelist[\xcnt,2,\ycnt,1]'' 
        has a value of ``\mynodelist[\xcnt,2,\ycnt,2]''.\\%
     }%
    \par%
  }%
}
\begin{document}
\mynodes{1:color=red,size=1cm;2:color=yellow,size=1cm}
\end{document}

With the single \readlist in the above code, all the inputs are immediately stored in the array \mynodelist.

\mynodelist[<node-number>,2,<key number>, 1] gives the associated key and

\mynodelist[<node-number>,2,<key number>, 2] gives the associated key value.

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.