6

I would like to factorize a little the code below.

\documentclass[12pt]{article}

\usepackage{tikz}

\newcommand\testabove[1]{above #1}
\newcommand\testbelow[1]{below #1}
\newcommand\testright[1]{right #1}
\newcommand\testleft[1]{left #1}

\begin{document}

\testabove{A} ;    
\testbelow{B} ;    
\testright{C} ;    
\testleft{D}

\end{document}

My idea is to use the \foreach loop of tikz but I am wrong with the following lines of codes. I am sure that I need to use \expandafter but any of my attempts has not worked.

Can I adapt the following lines such as to obtain my factorization ?

\foreach \kind in {above, below, right, left}{
    \newcommand\csname test\kind\endcsname[1]{\kind #1}
}

Using \expandafter\gdef\csname test\kind\endcsname##1{kind ##1} defines four macros, but not the right ones because I haven't use {\kind ##1}.

Indeed \expandafter\gdef\csname test\kind\endcsname##1{\kind ##1} gives the error message Undefined control sequence \testabove #1->\kind.

How can I make \kind be expanded in {\kind ##1} ?

  • 1
    Try doubling (or quadrupling or...) hashes... – Phelype Oleinik Oct 22 '19 at 10:28
  • @PhelypeOleinik I have updated my question by giving a less bad way to reach my goal at the ned of my question. – projetmbc Oct 22 '19 at 10:37
  • 1
    replace \gdef by \xdef – LaTeXer Oct 22 '19 at 10:59
  • Great ! What is the difference ? \gdef for no argument and \xdef for arguments ? – projetmbc Oct 22 '19 at 11:00
  • 1
    @projetmbc Sorry, I saw your comment just now. \gdef is the same as \global\def. \xdef is the same as \global\edef. \def simply defines whatever is in the argument and \edef expands first, which is what you want. \xdef to have the definition be global. – Phelype Oleinik Oct 22 '19 at 11:06
8

You can do without TikZ, and, what may be more important, avoid making the macros global. You can use the built-in \@for macro instead.

\documentclass{article}
\makeatletter
\@for\next:={above,below,right,left}\do{%
\expandafter\edef\csname test\next\endcsname#1{\next #1}}
\makeatother
\begin{document}
\testabove{A}     
\testbelow{B}     
\testright{C} 
\testleft{D}
\end{document}

There are many ways in which you may generalize this and/or make it more user-friendly. I do not know what the ultimate purpose will be, but this is to give you some idea.

\documentclass{article}
\makeatletter
\newcommand{\MultiDef}[3][]{\@for\next:=#3\do{%
\expandafter\edef\csname #2\next\endcsname##1{#1{\next} ##1}}}
\makeatother
\begin{document}
\MultiDef{test}{above,below,right,left}
\testabove{A}     
\testbelow{B}     
\testright{C} 
\testleft{D}

\MultiDef[hibernate ]{pft}{above,below,right,left}
\pftabove{A}     
\pftbelow{B}     
\pftright{C} 
\pftleft{D}
\end{document}
  • Thanks. The meta programming is fully achieved with your MultiDef macro. I will keep it in my mind. – projetmbc Oct 22 '19 at 15:22
  • What are the drawbacks of making global a macro ? – projetmbc Oct 22 '19 at 15:24
  • 2
    @projetmbc In general you not want to do that. For instance, if you set in a tikzpicture, say, the node distance to 1cm, this will apply to this tikzpicture only (or to the scope in which you set it). It will not affect the other tikzpictures you have in your document. If it did, this would be a mess, you add a picture with some keys (keys are macros internally), and this will change every other picture. Similarly, if you set \arraystretch in one table, you do not want this to affect every other table. Therefore it is arguably better to keep many things local. – Schrödinger's cat Oct 22 '19 at 15:28
  • @projetmbc In this sample document, this does not really matter since you define these things on the "outermost level". But it might matter if you accidentally overwrite existing macros. Again, if you only use these redefinitions locally, you may not break some stuff using the original definitions somewhere else. – Schrödinger's cat Oct 22 '19 at 15:30
  • Very clear comments. Thanks a lot ! – projetmbc Oct 22 '19 at 19:03
7

The \foreach function is meant for repetitive actions in TikZ pictures and has several drawbacks when employed elsewhere, the main one being that every cycle is performed in a group.

Here's a more general way to accomplish your needs; the defined macros can have as many arguments as you like (from none to nine, of course). In the fourth argument (the template) you denote by #1 the current item in the cycle and by ##1, ##2 and so on the defined macro arguments.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\batchdefine}{mO{0}mm}
 {% #1 is the prefix
  % #2 (default 0) is the number of arguments
  % #3 is a comma separated list
  % #4 is the template
  \clist_map_inline:nn { #3 } 
   {
    \projetmbc_batchdefine:cnn { #1 ##1 } { #2 } { #4 }
   }
 }

\cs_new_protected:Nn \projetmbc_batchdefine:Nnn
 {
  \newcommand{#1}[#2]{#3}
 }
\cs_generate_variant:Nn \projetmbc_batchdefine:Nnn { c }

\ExplSyntaxOff

\batchdefine{test}[1]{
  above, below, right, left
}{#1 ##1}

\batchdefine{TEST}[2]{
  above, below, right, left
}{##1 #1 ##2}

\begin{document}

\testabove{a} \testbelow{b} \testright{r} \testleft{l}

\TESTabove{a}{A} \TESTbelow{b}{B} \TESTright{r}{R} \TESTleft{l}{L}

\end{document}

enter image description here

  • One day I will learn the power LaTeX4-3 coding... One day. :-) – projetmbc Oct 22 '19 at 19:21
2

Thanks to the comments of Phelype Oleinik and LaTeXer, the following solution has been build.

\documentclass[12pt]{article}

\usepackage{tikz}

\foreach \kind in {above,below,right,left}{
    \expandafter\xdef\csname test\kind\endcsname##1{\kind ##1}
}


\begin{document}

\testabove{A} ;    
\testbelow{B} ;    
\testright{C} ;    
\testleft{D}

\end{document}

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.