2

I'd like to define a macro in which its name will be used as a logical reference.

For example, if the macro name is \mycmdAAA, it typeouts AAA; if the macro name is \mycmdBBB, it typesouts BBB; if the macro name is \mycmdCCC, it typesouts CCC.

That is to say, the name of the macro itself can be used in the part of the macro defination.

How to achieve this function?

  • 3
    If you want it to work with letters, as in your example, you'd have to define multiple commands with the same prefix \mycmd. You could only make it work if the first character of the argument is something that's not defined to be a letter currently. Why not just using \mycmd{XXX}? – siracusa Oct 24 '18 at 3:11
  • 3
    Can you give more detail and, ideally, a minimal working example, that shows what you want do with such a macro. As stated, \newcommand\mycmdAAA{AAA} \newcommand\mycmdBBB{BBB} seems to fulfill the aims of your question but I doubt that this is what you want. Perhaps the macro \newcommand\BuildCmd[1]{\csdef{mycmd#1}{#1}}, followed by \BuildCmd{AAA}\BuildCmd{BBB}, does what you need? For \BuildCmd to work you will need \usepackage{etoolbox} to load the etoolbox package. – Andrew Oct 24 '18 at 4:16
  • @ siracusa Some tasks are similar. They have almost the same steps except some difference. I don't like to repeat the same thing again and again. That's why I pose this question. – lyl Oct 24 '18 at 6:28
  • Great solution! Thank you very much @Andrew. Still, if some other arguments like "CCC", "DDD", "EEE", "FFF" ... are needed, \BuildCmd{...} will repeat many times. Is there a better way that only one step is enough? – lyl Oct 24 '18 at 6:33
  • @Andrew I mean for example, \mycmdZZZ will automatically be defined when it is called. – lyl Oct 24 '18 at 6:42
3

I am still not quite sure what is required but this is too long for a comment and may answer the question.

In the comments to the OP it is asked if it is possible to have a command define itself when it is first used. As far as I am aware this is not possible, however, it is possible to define a whole bunch of similar commands at once using something like the command below.

\documentclass{article}
\usepackage{etoolbox}
\newcommand\BuildCommands[1]{% usage: \BuildCommands{comma separated list}
  \renewcommand*\do[1]{\csdef{mycmd##1}{##1}}%
  \docsvlist{#1}%
}

\BuildCommands{AAA,BBB,CCC,DDD,EEE}
\begin{document}

  \mycmdAAA
  \mycmdBBB
  \mycmdCCC
  \mycmdDDD
  \mycmdEEE

\end{document}

The trick is the \docsvlist command from the etoolbox package, which loops over a comma separated list to define commands of the form \mycmdXXX where XXX comes from the CSV.

Perhaps this meets the OP's requirements? If this does answer the question then, as @siracusa comments above, it is not clear to me why it is not enough to use something like:

\newcommand\mycmd[1]{#1}
\mycmd{AAA} \mycmd{BBB} ...

Edit

To anwser the question in the comments to make \BuildCommands both define a \mycmd macro and insert the macro name into the text requires only a small change to the code above. Note, however, that now \BuildCommands can only be used after \begin{document} since it is inserting text. Here is the revised code:

\documentclass{article}
\usepackage{etoolbox}
\newcommand\BuildCommands[1]{% usage: \BuildCommands{comma separated list}
  \renewcommand*\do[1]{\csdef{mycmd##1}{##1}##1}%
  \docsvlist{#1}%
}

\begin{document}
\BuildCommands{AAA,BBB,CCC,DDD,EEE}

  \mycmdAAA
  \mycmdBBB
  \mycmdCCC
  \mycmdDDD
  \mycmdEEE

\end{document}
  • Why the following code fails to compile:\newcommand\BuildCmd[1]{\csdef{mycmd#1}{\ifthenelse{\equal{#1}{table}}{table}{figure}}},followed by \BuildCmd{table} \BulidCmd{figure}. The original #1 is replaced by \ifthenelse. – lyl Oct 24 '18 at 7:23
  • @lyl \ifthenelse cannot be used in the context of \csdef. Use \ifstrequal instead (described in the manual for etoolbox). – egreg Oct 24 '18 at 7:30
  • How to get the result directly from \BuildCommands? I mean when calling \BuildCommands{AAA,BBB,CCC,DDD,EEE}, the typeset will be "AAABBBCCCDDDEEE". – lyl Feb 8 at 13:46
  • @lyl I don't understand what you are asking. Do you want \BuildCommands{AAA,BBB,CCC,DDD,EEE} to put AAABBBCCCDDDEEE and, at the same time, define \mycmdAAA etc? – Andrew Feb 8 at 21:39
  • Yes@Andrew, that's what I want – lyl Feb 9 at 0:49
3

A code that does what you want and also copes with exceptions. In the example I define to families of commands, one with “exceptions” and one without.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\BuildCommands}{mmO{}}
 {% #1 = prefix,
  % #2 = list,
  % #3 (optional) = exceptions
  \clist_map_inline:nn { #2 }
   {
    \cs_new:cpx { #1##1 } { \str_case:nnF {##1}{#3}{##1} }
   }
 }
\ExplSyntaxOff

\BuildCommands{my}{AAA,BBB,table,foo}[
  {table}{figure}
  {foo}{baz}
]
\BuildCommands{our}{AAA,BBB,table,foo}

\begin{document}

\myAAA \par
\myBBB \par
\mytable \par
\myfoo \par

\ourAAA \par
\ourBBB \par
\ourtable \par
\ourfoo \par

\end{document}

enter image description here

  • Many @egreg. Could you please provide the definition of \BuildCommands in another way because I don't understand code of Latex 3, – lyl Oct 24 '18 at 7:59
  • @lyl No, the support for case switching is not available with other frameworks, I'd not go into defining it from scratch. – egreg Oct 24 '18 at 8:36
2

If it was possible that a macro/a command could evaluate its own name, then the following question would arise:

What behavior would you wish for \mycmdDDD when via \let\mycmdDDD=\mycmdCCC creating a control sequence \mycmdDDD whose meaning equals the meaning of \mycmdCCC? Should \mycmdDDD type out CCC like \mycmdCCC or should it type out DDD?


Here is an implementation in plain TeX of a loop for defining many similar commands.

You can specify exceptions by adding to the macros \Exceptionfork and \ForkExceptions.

It is assumed that strings denoting the distinguishable parts of the macro-names and exceptions don't contain the token \dLm.

You don't need to stick to the token \dlm. As delimiter you can choose any set of tokens that does not occur within any of the strings that denote the distinguishable parts of macro-names and exceptions.

This is the sort of quick'n-dirty-thing that I would probably do when creating a document whereof nobody but me will get the .tex-sources but that I would not do when writing code for a package or the like that is to be released to the public. ;-)

\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\exchange#1#2{#2#1}%
\long\def\passfirsttosecond#1#2{#2{#1}}%
%%-------------------------------------------------------------------------
%% Check whether argument is empty:
%%.........................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is not empty>}%
\long\def\CheckWhetherNull#1{%
  \romannumeral0\expandafter\secondoftwo\string{\expandafter
  \secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \secondoftwo\string}\expandafter\firstoftwo\expandafter{\expandafter
  \secondoftwo\string}\expandafter\expandafter\firstoftwo{ }{}%
  \secondoftwo}{\expandafter\expandafter\firstoftwo{ }{}\firstoftwo}%
}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%-------------------------------------------------------------------------
%% Check whether argument contains no token \dLm on top-brace-level:
%%.........................................................................
%% \CheckWhetherNoDelimiter{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked does not contain \dLm>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked does contain \dLm>}%
\long\def\RemoveToDelimiter#1\dLm{}%
\long\def\CheckWhetherNoDelimiter#1{%
  \expandafter\CheckWhetherNull\expandafter{\RemoveToDelimiter#1\dLm}%
}%
%%-------------------------------------------------------------------------
%% Create control-sequence-token from macro name:
%% \name{foo} -> \foo
%% \name\newcommand{foo}... -> \newcommand\foo....
%%-------------------------------------------------------------------------
\long\def\name#1#{\romannumeral0\innername{#1}}%
\long\def\innername#1#2{%
  \expandafter\exchange\csname#2\endcsname{ #1}%
}%
%%-------------------------------------------------------------------------
%% Tail-recursive loop for defining similar commands:
%%-------------------------------------------------------------------------
%% \dlm denotes the end of the comma-list:
\long\def\Definecommands#1{\Definecommandsloop#1,\dLm,}%
%%
\long\def\Definecommandsloop#1,{%
  \CheckWhetherNoDelimiter{#1}%
  {%
    \expandafter\expandafter\expandafter\passfirsttosecond
    \expandafter\expandafter\expandafter{%
    \ForkExceptions{#1}}%
    {\name\long\def{Mycmd#1}}%
    \Definecommandsloop
  }{}%
}%
%%-------------------------------------------------------------------------
\long\def\Exceptionfork#1\dLm\dLm DDD\dLm EEE\dLm#2#3\dLm\dLm\dLm\dLm{ #2}% The space before #2 stops \romannumeral-expansion.
\long\def\ForkExceptions#1{%
  \romannumeral0%
  \CheckWhetherNoDelimiter{#1}{%
    \Exceptionfork
    \dLm#1\dLm DDD\dLm EEE\dLm{Definition text for case emptiness}%<- Definition text in case of empty argument
    \dLm  \dLm  #1\dLm EEE\dLm{Definition text for exceptional case DDD}%<-Definition text in case DDD
    \dLm  \dLm DDD\dLm  #1\dLm{Definition text for exceptional case EEE}%<-Definition text in case EEE
    \dLm  \dLm DDD\dLm EEE\dLm{#1}%<- Definition text for other cases without \dLm
    \dLm\dLm\dLm\dLm
  }{ #1}%<- Definition text for other cases with \dLm. The space before #1 stops \romannumeral-expansion.
}%



\tt\frenchspacing

\string\Definecommands\string{AAA,BBB,CCC,DDD,EEE,FFF,,!!!,\string{,,,\string}\string}

\Definecommands{AAA,BBB,CCC,DDD,EEE,FFF,,!!!,{,,,}}

\bigskip

\string\MycmdAAA: \meaning\MycmdAAA

\string\MycmdBBB: \meaning\MycmdBBB

\string\MycmdCCC: \meaning\MycmdCCC

\string\MycmdDDD: \meaning\MycmdDDD

\string\MycmdEEE: \meaning\MycmdEEE

\string\MycmdFFF: \meaning\MycmdFFF

\string\Mycmd: \meaning\Mycmd

\name\string{Mycmd!!!}: \name\meaning{Mycmd!!!}

\name\string{Mycmd,,,}: \name\meaning{Mycmd,,,}


\bye

enter image description here


Of course you can use the forking-technique from the previous example also for defining a macro that processes one argument and forks depending on that.

In the following example I combined the forking-technique from the previous example with a routine for picking the K-th of L consecutive undelimited arguments.

%%=============================================================================
%% "Paraphernalia":
%%=============================================================================
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\exchange#1#2{#2#1}%
\long\def\passfirsttosecond#1#2{#2{#1}}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is not empty>}%
\long\def\CheckWhetherNull#1{%
  \romannumeral0\expandafter\secondoftwo\string{\expandafter
  \secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \secondoftwo\string}\expandafter\firstoftwo\expandafter{\expandafter
  \secondoftwo\string}\expandafter\expandafter\firstoftwo{ }{}%
  \secondoftwo}{\expandafter\expandafter\firstoftwo{ }{}\firstoftwo}%
}%
%%=============================================================================
%% Keep only the K-th of L consecutive undelimited arguments.
%%   ( IF K < 1 OR K > L just remove L consecutive undelimited arguments. )
%%=============================================================================
%% \KeepKthOfLArguments{<integer number K>}%
%%                     {<integer number L>}%
%%                     <sequence of L consecutive undelimited arguments>
%%
%% If L < 1 yields nothing.
%% Else:
%%   If K >= 1 and K <= L  yields:
%%     <K-th undelimited argument from <sequence of L consecutive undelimited 
%%      arguments>>
%%   If K < 1 or K > L
%%     (-> there is no K-th argument in the
%%         <sequence of L consecutive undelimited arguments> )
%%   yields nothing  but removal of <sequence of L consecutive 
%%          undelimited arguments>
\long\def\KeepKthOfLArguments#1#2{%
  \romannumeral0%
  % #1: <integer number K>
  % #2: <integer number L>
  \expandafter\UDKeepKthOfLArgumentsKSmallerOneFork
  \expandafter{\romannumeral\number\number#1 000\expandafter}%
  \expandafter{\romannumeral\number\number#2 000}%
}%
%%-----------------------------------------------------------------------------
\long\def\UDKeepKthOfLArgumentsKSmallerOneFork#1#2{%
  % #1: <K letters m>
  % #2: <L letters m >
  \CheckWhetherNull{#1}{% K is smaller than one:
    \UDKeepKthOfLArgumentsRemoveNArguments{#2}{ }{}%
  }{% K is not smaller than one:
    \expandafter\passfirsttosecond
    \expandafter{%
      \firstoftwo{}#1%
    }{%
      \UDKeepKthOfLArgumentsEvaluateLMinusKDifferenceLoop{#1}{#2}%
    }{#2}%
  }%
}%
%%-----------------------------------------------------------------------------
\long\def\UDKeepKthOfLArgumentsEvaluateLMinusKDifferenceLoop#1#2#3#4{%
  % #1: <K letters m>  
  % #2: <L letters m>
  % (For detecting whether K>L or K<=L, during the loop letters m will
  %  be removed both from #1 and #2 until at least one of these arguments 
  %  is empty.
  %  When the loop terminates with 0<K<=L, #1 will be empty and #2
  %  will hold an amount of letters m corresponding to the the 
  %  difference L-K.
  %  When the loop terminates with K>L, #1 will not be empty and #2
  %  will be empty.
  % )
  % #3: <K-1 letters m>
  % #4: <L letters m>
  % (#3 and #4 will be left untouched during the loop so they can be 
  %  used for performing appropriate action when loop terminates as
  %  it is known whether K>L.)
  \CheckWhetherNull{#1}{% We have K<=L:
     \UDKeepKthOfLArgumentsRemoveNArguments{%
       #3%
      }{%
       \UDKeepKthOfLArgumentsRemoveNArguments{#2}{ }%
      }{}%
  }{%
    \CheckWhetherNull{#2}{% We have K>L:
      \UDKeepKthOfLArgumentsRemoveNArguments{#4}{ }{}%
    }{% We don't know yet whether K<=L or K>L, thus remove letters m and 
      % do another iteration:
      \expandafter\passfirsttosecond
      \expandafter{%
        \firstoftwo{}#2%
      }{%
        \expandafter\UDKeepKthOfLArgumentsEvaluateLMinusKDifferenceLoop
        \expandafter{%
          \firstoftwo{}#1%
        }%
      }{#3}{#4}%
    }%
  }%
}%
%%-----------------------------------------------------------------------------
%% \UDKeepKthOfLArgumentsRemoveNArguments{<N letters m>}%
%%                                       {<argument 1>}%
%%                                       {<argument 2>}%
%%                                       <sequence of consecutive 
%%                                        undelimited arguments>
%%.............................................................................
%% Removes the first N undelimited arguments from the <sequence of 
%% consecutive undelimited arguments>, then inserts  
%% <argument 1><argument 2>
%%
%% On the one hand when providing <argument 2> empty, you can use 
%% <argument 1> for nesting calls to \UD@KeepKthOfLArgumentsRemoveNArguments.
%% On the other hand you can provide a <space token> for stopping
%% \romannumeral-expansion as  <argument 1> and have the
%% macro grab the <K-th undelimited argument> from the <sequence of L 
%% consecutive undelimited arguments> as <argument 2>.
%%
\long\def\UDKeepKthOfLArgumentsRemoveNArguments#1#2#3{%
  %% #1: <N letters m>  
  %% #2: <Argument 1>   
  %% #3: <Argument 2>
  \CheckWhetherNull{#1}{#2#3}{%
    \firstoftwo{%
      \expandafter\UDKeepKthOfLArgumentsRemoveNArguments
      \expandafter{%
        \firstoftwo{}#1%
      }{#2}{#3}%
    }%
  }%
}%
%%=============================================================================
%% Forking-mechanism:
%%=============================================================================
%% Check whether argument contains no token \dLm on top-brce-level:
%%
%% \CheckWhetherNoDelimiter{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked does not contain \dLm>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked does contain \dLm>}%
\long\def\RemoveToDelimiter#1\dLm{}%
\long\def\CheckWhetherNoDelimiter#1{%
  \expandafter\CheckWhetherNull\expandafter{\RemoveToDelimiter#1\dLm}%
}%
%% Fork via delimited arguments:
\long\def\ForkMyCmd#1\dLm\dLm DDD\dLm EEE\dLm#2#3\dLm\dLm\dLm\dLm{#2}%
\long\def\MycmdFork#1{%
  \KeepKthOfLArguments{%
    % \KeepKthOfLArguments does evaluate its number-arguments via
    % \romannumeral/\number-expansion.
    % Thus we can here use the forking technique for selecting the
    % appropriate value K for laterwards picking the K-th argument:
    \CheckWhetherNoDelimiter{#1}{%
      \ForkMyCmd
      \dLm#1\dLm DDD\dLm EEE\dLm{2}%<-argument to pick in case of empty argument
      \dLm  \dLm  #1\dLm EEE\dLm{3}%<-argument to pick in case DDD
      \dLm  \dLm DDD\dLm  #1\dLm{4}%<-argument to pick in case EEE
      \dLm  \dLm DDD\dLm EEE\dLm{1}%<-argument to pick for other cases without \dLm
      \dLm\dLm\dLm\dLm
    }{1}%<- argument to pick for other cases with \dLm
  }{4}% We will have L=4 cases/arguments
}%
\long\def\Mycmd#1{%
  \MycmdFork{#1}% argument to check.
  {#1}% text in all other cases.
  {text in exception-case argument is empty}%
  {text in exception-case argument is DDD}%
  {text in exception-case argument is EEE}%   
}%    


\tt\frenchspacing

\string\Mycmd\string{AAA\string}: \Mycmd{AAA}

\string\Mycmd\string{BBB\string}: \Mycmd{BBB}

\string\Mycmd\string{CCC\string}: \Mycmd{CCC}

\string\Mycmd\string{DDD\string}: \Mycmd{DDD}

\string\Mycmd\string{EEE\string}: \Mycmd{EEE}

\string\Mycmd\string{FFF\string}: \Mycmd{FFF}

\string\Mycmd\string{\string}: \Mycmd{}

\string\Mycmd\string{!!!\string}: \Mycmd{!!!}

\string\Mycmd\string{,,,\string}: \Mycmd{,,,}

\bye

enter image description here

2

A listofitems approach.

\documentclass{article}
\usepackage{listofitems}
\newcommand\BuildCommands[1]{%
  \readlist*\bldargs{#1}%
  \foreachitem\x\in\bldargs[]{%
    \expandafter\def\csname mycmd\x\expandafter\endcsname\expandafter{\x}%
  }%
}
\BuildCommands{AAA,BBB,CCC,DDD,EEE}
\begin{document}
  \mycmdAAA
  \mycmdBBB
  \mycmdCCC
  \mycmdDDD
  \mycmdEEE
\end{document}

enter image description here

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.