3

How does one write an if clause that checks whether the first element of the immediate expansion is \mymacro, without fully expanding the input?

\documentclass{minimal}

\def\mymacro#1{Does something with #1}
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}

\newcommand{\startswithmymacro}[1]{%
    % tests if the immediate expansion of #1 starts with \mymacro
    % 1. expand #1 once (something with \expandonce??)
    % 2. store first token, discard the rest (something with \gobble??)
    % 3. compare first token with \mymacro (\ifx\first\mymacro)
}

\begin{document}
\startswithmymacro{\foo} % true
\startswithmymacro{\bar} % true
\startswithmymacro{\baz} % true
\startswithmymacro{\qux} % true
\startswithmymacro{\mymacrosecond} % false
\startswithmymacro{\nomymacro} % false
\startswithmymacro{\hiddenmymacro} % false
\end{document}
2
  • 1
    Assume \def\bat{{\mymacro}{1}}. How do you want \startswithmymacro{\bat} to behave? Feb 21 at 2:16
  • 1
    How do you want \startswithmymacro to behave in case \mymacro is undefined? (Even when the token \mymacro is undefined it may still occur as the first token of \startswithmymacro's argument's first token's expansion.) Feb 21 at 2:46

4 Answers 4

4

You've actually described the steps pretty well yourself. The only thing missing is the code implementing them :-)

\documentclass{article}

\def\mymacro#1{Does something with #1}
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}

\makeatletter
\newcommand{\startswithmymacro}[1]{%
   \begingroup
   \edef\@tempa{\unexpanded\expandafter{#1}}%
   \def\@tempb##1##2\quark{\def\@tempc{##1}}%
   \expandafter\@tempb\@tempa\quark
   \expandafter\ifx\@tempc\mymacro T\else F\fi
   \endgroup
}
\makeatother

\begin{document}
(\startswithmymacro{\foo},T) % true
(\startswithmymacro{\bar},T) % true
(\startswithmymacro{\baz},T) % true
(\startswithmymacro{\qux},T) % true
(\startswithmymacro{\mymacrosecond},F) % false
(\startswithmymacro{\nomymacro},F) % false
(\startswithmymacro{\hiddenmymacro},F) % false
\end{document}

enter image description here

5
  • Thank you, I wouldn't have been able to come up with that, looks very tricky. What exactly does \edef\@tempa{\unexpanded\expandafter{#1}}% do? Like, how is it different from \def\@tempa{\expandafter{#1}} ?
    – Hyperplane
    Feb 20 at 13:12
  • 1
    @Hyperplane \unexpanded\expandafter{#1} is how the package etoolbox defines \expandonce. The \expandafter freezes the opening brace, so that #1 gets expanded. Then the balanced text goes to \unexpanded and thus isn't further expanded.
    – campa
    Feb 20 at 13:25
  • Ohhhhh. I was wondering about those braces too. \expandafter just makes my brain hurt after a while...
    – Hyperplane
    Feb 20 at 13:28
  • @Hyperplane .\edef\@tempa{\unexpanded\expandafter{#1}} doubles hashes (#) in case hashes are among the tokens forming \startswithmymacro's argument #1. Assume \startswithmacro is called as \startswithmacro{\macro#1b#2c#3}. You get: \edef\@tempa{\unexpanded\expandafter{\macro#1b#2c#3}}. This is like \def\@tempa{<toplevel expansion of macro>##1b##2c##3}. So #1, #2, #3 are not taken for parameters of \@tempa not declared in \@tempa's <parameter text> but amounts of consecutive hashes are halved when expanding \@tempa to yield <toplevel expansion of macro>#1b#2c#3. Feb 21 at 1:03
  • 1
    @campa With \def\bat{{\mymacro}{1}} the call (\startswithmymacro{\bat},F) yields (T,F). With \def\bat{{xx\fi}} the call (\startswithmymacro{\bat},F) yields error messages. Feb 21 at 3:04
5

You can use \futurelet TeX primitive after the argument is expanded once by the \expandafter chain:

\def\startswithtokenmymacro#1{%
   \expandafter\futurelet\expandafter\next\expandafter\swtmmA#1\swtmmA}
\def\swtmmA#1\swtmmA{\ifx\next\mymacro TRUE\else FALSE\fi}

%% Tests:

\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\def\mymacroinbraces{{\mymacro{1}}something}
\def\mymacrocopyfoo{\mymacrocopy{1}}
\def\mymacrocopybar{\mymacrocopy{2}}
\def\mymacrocopybaz{\mymacrocopy{3}}
\def\mymacrocopyqux{\mymacrocopy{4}\extra}
\def\mymacrosecond{\something\mymacrocopy{3}}
\def\mymacrocopyinbraces{{\mymacrocopy{1}}something}
\def\bat{{\mymacro}{1}}%
\def\bau{{xx\fi}}
\def\bav{}
\def\baw{ }

\startswithtokenmymacro{\foo} - expected: true

\startswithtokenmymacro{\bar} - expected: true

\startswithtokenmymacro{\baz} - expected: true

\startswithtokenmymacro{\qux} - expected: true

\startswithtokenmymacro{\romannumeral0 \mymacro} - expected: true

\startswithtokenmymacro{\csname mymacro\endcsname} - expected: true

\startswithtokenmymacro{\mymacrosecond} - expected: false

\startswithtokenmymacro{\nomymacro} - expected: false

\startswithtokenmymacro{\hiddenmymacro} - expected: false

\startswithtokenmymacro{\mymacroinbraces} - expected: false

\startswithtokenmymacro{\mymacrocopyfoo} - expected: false

\startswithtokenmymacro{\mymacrocopybar} - expected: false

\startswithtokenmymacro{\mymacrocopybaz} - expected: false

\startswithtokenmymacro{\mymacrocopyqux} - expected: false

\startswithtokenmymacro{\mymacrosecond} - expected: false

\startswithtokenmymacro{\mymacrocopyinbraces} - expected: false

\startswithtokenmymacro{{\foo}} - expected: false

\startswithtokenmymacro{\bat} - expected: false

\startswithtokenmymacro{\bau} - expected: false

\startswithtokenmymacro{\bav} - expected: false

\startswithtokenmymacro{\baw} - expected: false

\bye
1
  • Thank you for this answer as well. Comparing yours and campa's, it seems I have two different ways to extract the first token: Either define \def\drop@start#1\drop@stop{} which just drops everything between the two separators and then \newcommand{\getfirsttok}[2]{\expandafter\futurelet\expandafter#1\expandafter\drop@start#2\drop@stop} , or using a \def and using the second argument to gobble things: \newcommand{\letfirsttok}[2]{% \def\@extract##1##2\@terminator{\let#1=##1}% \expandafter\@extract#2\@terminator% } Is there any meaningful difference between these?
    – Hyperplane
    Feb 21 at 22:21
1

(In most situations in practical life expandability and a sentinel token being placed behind the last token of the argument is not an issue. So in practical life wipet's answer definitely is to be preferred. My answer is for the case that expandability is an issue due to need (which seems unlikely) or due to "academical interest".)



In case you wish to test whether the result of "hitting" \startswithtokenmymacro's argument's first token with \expandafter has a first token which is exactly the control word token \mymacro, the following might do the trick as long as none of the tokens of that result is \outer:

\makeatletter
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@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>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument has a first token which
%% is exactly the token \mymacro
%%.............................................................................
%% \UDCheckWhetherLeadingMymacro{<Argument which is to be checked>}%
%%                              {<Tokens to be delivered in case <argument
%%                                which is to be checked> does have a
%%                                leading \mymacro>}%
%%                              {<Tokens to be delivered in case <argument
%%                                which is to be checked> does not have a
%%                                a leading \mymacro>}%
\newcommand\UDCheckWhetherLeadingMymacro[1]{%
  \romannumeral\UD@CheckWhetherNull{#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
  {%
    % Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\UD@firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \romannumeral\expandafter\UD@secondoftwo
      \string{\UD@CheckWhetherLeadingMymacroB.#1\mymacro}{}%
    }{}%
  }%
}%
\@ifdefinable\UD@CheckWhetherLeadingMymacroB{%
  \long\def\UD@CheckWhetherLeadingMymacroB#1\mymacro{%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
    {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
    {\expandafter\expandafter\expandafter\UD@stopromannumeral
     \expandafter\expandafter\expandafter}%
     \expandafter\UD@secondoftwo\expandafter{\string}%
  }%
}%
%%=============================================================================
\makeatother

\documentclass{minimal}

%% As the test is on the presence of tokens, not on meanings of possibly
%% defined macros, the following two definitions are not needed:
%\def\mymacro#1{Does something with #1}
%\let\mymacrocopy=\mymacro
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\def\mymacroinbraces{{\mymacro{1}}something}
\def\mymacrocopyfoo{\mymacrocopy{1}}
\def\mymacrocopybar{\mymacrocopy{2}}
\def\mymacrocopybaz{\mymacrocopy{3}}
\def\mymacrocopyqux{\mymacrocopy{4}\extra}
\def\mymacrosecond{\something\mymacrocopy{3}}
\def\mymacrocopyinbraces{{\mymacrocopy{1}}something}
\def\bat{{\mymacro}{1}}%
\def\bau{{xx\fi}}
\def\bav{}
\def\baw{ }

\newcommand{\startswithtokenmymacro}[1]{%
  \expandafter\UDCheckWhetherLeadingMymacro\expandafter{#1}{true}{false}%
}

\begin{document}
\startswithtokenmymacro{\foo} - expected: true

\startswithtokenmymacro{\bar} - expected: true

\startswithtokenmymacro{\baz} - expected: true

\startswithtokenmymacro{\qux} - expected: true

\startswithtokenmymacro{\romannumeral0 \mymacro} - expected: true

\startswithtokenmymacro{\csname mymacro\endcsname} - expected: true

\startswithtokenmymacro{\mymacrosecond} - expected: false

\startswithtokenmymacro{\nomymacro} - expected: false

\startswithtokenmymacro{\hiddenmymacro} - expected: false

\startswithtokenmymacro{\mymacroinbraces} - expected: false

\startswithtokenmymacro{\mymacrocopyfoo} - expected: false

\startswithtokenmymacro{\mymacrocopybar} - expected: false

\startswithtokenmymacro{\mymacrocopybaz} - expected: false

\startswithtokenmymacro{\mymacrocopyqux} - expected: false

\startswithtokenmymacro{\mymacrosecond} - expected: false

\startswithtokenmymacro{\mymacrocopyinbraces} - expected: false

\startswithtokenmymacro{{\foo}} - expected: false

\startswithtokenmymacro{\bat} - expected: false

\startswithtokenmymacro{\bau} - expected: false

\startswithtokenmymacro{\bav} - expected: false

\startswithtokenmymacro{\baw} - expected: false
\end{document}


In case you wish to test whether the result of "hitting" \startswithmeaningofmymacro's argument's first token with \expandafter has a first token which has the same meaning as the token \mymacro, the following might do the trick as long as none of the tokens of that result is \outer:

Check whether that result is empty. If this is the case, then it is not the case that that result has a first token which has the same meaning as the token \mymacro. If that result is not empty, check whether that result's first token is an explicit character token of category 1 (begin group), e.g., a curly left brace {1. If this is the case, then it is not the case that that result has a first token which has the same meaning as the token \mymacro. If this is not the case, then check whether that result's first token is an explicit space token. If this is the case, then it is not the case that that result has a first token which has the same meaning as the token \mymacro. If this is not the case, then the first token of that result can safely be extracted by means of undelimited macro arguments and brace hacks and afterwards its meaning can be compared via \ifx to the meaning of the token \mymacro:

\makeatletter
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, \UD@Exchange,
%% \UD@stopromannumeral, \UD@CheckWhetherNull,
%% \UD@CheckWhetherBrace, \UD@CheckWhetherLeadingExplicitSpace,
%% \UD@CheckWhetherMeaningsEqual, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@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>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is an explicit character token 
%% of category 1 (begin group):
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has a leading
%%                        explicit catcode-1-character-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked does not have a
%%                        leading explicit catcode-1-character-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@firstoftwo}{%
  \expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument's first token is an explicit
%% space-token:
%%.............................................................................
%% \UD@CheckWhetherLeadingExplicitSpace{<Argument which is to be checked>}%
%%                                     {<Tokens to be delivered in case <argument
%%                                       which is to be checked> does have a
%%                                       leading explicit space-token>}%
%%                                     {<Tokens to be delivered in case <argument
%%                                       which is to be checked> does not have a
%%                                       a leading explicit space-token>}%
\newcommand\UD@CheckWhetherLeadingExplicitSpace[1]{%
  \romannumeral\UD@CheckWhetherNull{#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
  {%
    % Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\UD@firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \romannumeral\expandafter\UD@secondoftwo
      \string{\UD@CheckWhetherLeadingExplicitSpaceB.#1 }{}%
    }{}%
  }%
}%
\@ifdefinable\UD@CheckWhetherLeadingExplicitSpaceB{%
  \long\def\UD@CheckWhetherLeadingExplicitSpaceB#1 {%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
    {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
    {\expandafter\expandafter\expandafter\UD@stopromannumeral
     \expandafter\expandafter\expandafter}%
     \expandafter\UD@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Check whether meanings of two tokens are equal.
%%.............................................................................
\newcommand\UD@CheckWhetherMeaningsEqual[2]{%
  \romannumeral
  \expandafter\UD@firstoftwo\expandafter{%
    \romannumeral\ifx#1#2%
    \expandafter\UD@stopromannumeral\expandafter\expandafter
    \expandafter\UD@stopromannumeral\expandafter\UD@firstoftwo\else
    \expandafter\UD@stopromannumeral\expandafter\expandafter
    \expandafter\UD@stopromannumeral\expandafter\UD@secondoftwo\fi
  }{}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {AB}
%%
%% Due to \romannumeral-expansion the result is delivered after two 
%% expansion-steps/after "hitting" \ExtractFirstArg with \expandafter
%% twice.
%%
%% \UD@ExtractFirstArg's argument must not be blank.
%%
%% Use frozen-\relax as delimiter for speeding things up.
%% I chose frozen-\relax because David Carlisle pointed out in
%% <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
%%
%%.............................................................................
\@ifdefinable\UD@RemoveTillFrozenrelax{%
  \expandafter\expandafter\expandafter\UD@Exchange
  \expandafter\expandafter\expandafter{%
  \expandafter\expandafter\ifnum0=0\fi}%
  {\long\def\UD@RemoveTillFrozenrelax#1#2}{{#1}}%
}%
\expandafter\UD@PassFirstToSecond\expandafter{%
  \romannumeral\expandafter
  \UD@PassFirstToSecond\expandafter{\romannumeral
    \expandafter\expandafter\expandafter\UD@Exchange
    \expandafter\expandafter\expandafter{%
    \expandafter\expandafter\ifnum0=0\fi}{\UD@stopromannumeral#1{}}%
  }{%
    \UD@stopromannumeral\romannumeral\UD@ExtractFirstArgLoop
  }%
}{%
  \newcommand\UD@ExtractFirstArg[1]%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@stopromannumeral#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%%=============================================================================
%% Check whether brace-balanced argument has a first token whose meaning
%% equals the meaning of the token \mymacro
%%.............................................................................
%% \UDCheckWhetherLeadingMymacroMeaning
%%   {<Argument which is to be checked>}%
%%   {<Tokens to be delivered in case <argument which is to be checked> does
%%     have a first token whose meaning equals the meaning of the token
%%     \mymacro>}%
%%   {<Tokens to be delivered in case <argument which is to be checked> does
%%     not have a first token whose meaning equals the meaning of the token
%%     \mymacro>}%
\newcommand\UDCheckWhetherLeadingMymacroMeaning[1]{%
  \romannumeral\UD@CheckWhetherNull{#1}{\UD@secondoftwo}%
  {%
     \UD@CheckWhetherBrace{#1}{\UD@secondoftwo}%
     {%
       \UD@CheckWhetherLeadingExplicitSpace{#1}{\UD@secondoftwo}%
       {%
         \expandafter\expandafter\expandafter
         \UD@CheckWhetherMeaningsEqual
         \UD@ExtractFirstArg{#1}{\mymacro}{\UD@firstoftwo}{\UD@secondoftwo}%
       }%
     }%
  }%
  {\expandafter\UD@stopromannumeral\UD@firstoftwo}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%
%%=============================================================================
\makeatother

\documentclass{minimal}

%% As the test is on meanings of tokens, he following two definitions
%% affect the resukts of the test:
\def\mymacro#1{Does something with #1}
\let\mymacrocopy=\mymacro
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\def\mymacroinbraces{{\mymacro{1}}something}
\def\mymacrocopyfoo{\mymacrocopy{1}}
\def\mymacrocopybar{\mymacrocopy{2}}
\def\mymacrocopybaz{\mymacrocopy{3}}
\def\mymacrocopyqux{\mymacrocopy{4}\extra}
\def\mymacrosecond{\something\mymacrocopy{3}}
\def\mymacrocopyinbraces{{\mymacrocopy{1}}something}
\def\bat{{\mymacro}{1}}
\def\bau{{xx\fi}}
\def\bav{}
\def\baw{ }

\newcommand{\startswithmeaningofmymacro}[1]{%
  \expandafter\UDCheckWhetherLeadingMymacroMeaning\expandafter{#1}{true}{false}%
}

\begin{document}

\ttfamily

\startswithmeaningofmymacro{\foo} - expected: true

\startswithmeaningofmymacro{\bar} - expected: true

\startswithmeaningofmymacro{\baz} - expected: true

\startswithmeaningofmymacro{\qux} - expected: true

\startswithmeaningofmymacro{\mymacrocopyfoo} - expected: true

\startswithmeaningofmymacro{\mymacrocopybar} - expected: true

\startswithmeaningofmymacro{\mymacrocopybaz} - expected: true

\startswithmeaningofmymacro{\mymacrocopyqux} - expected: true

\startswithmeaningofmymacro{\romannumeral0 \mymacro} - expected: true

\startswithmeaningofmymacro{\romannumeral0 \mymacrocopy} - expected: true

\startswithmeaningofmymacro{\csname mymacro\endcsname} - expected: true

\startswithmeaningofmymacro{\csname mymacrocopy\endcsname} - expected: true

\startswithmeaningofmymacro{\mymacrosecond} - expected: false

\startswithmeaningofmymacro{\nomymacro} - expected: false

\startswithmeaningofmymacro{\hiddenmymacro} - expected: false

\startswithmeaningofmymacro{\mymacroinbraces} - expected: false

\startswithmeaningofmymacro{\mymacrocopyinbraces} - expected: false

\startswithmeaningofmymacro{{\foo}} - expected: false

\startswithmeaningofmymacro{\bat} - expected: false

\startswithmeaningofmymacro{\bau} - expected: false

\startswithmeaningofmymacro{\bav} - expected: false

\startswithmeaningofmymacro{\baw} - expected: false
\end{document}

When looking at the code you sometimes see things wrapped in an extra \UD@firstoftwo{...}{} or the like. I did this to ensure that tokens provided as macro arguments by the user after every expansion step except the last one which delivers the result are nested between a pair of matching curly braces. This in turn is for the case of the macro-mechanism being applied while LaTeX is scanning for elements of an alignment which in turn happens in the course of carrying out tabular environments and the like. In this situation, in case things were not nested between braces, the expansion-cascade initiated by the macro mechanism might be intercepted by TeX's scanning for elements of the alignment.



By the way: If you are picky, then you can say that \ifx is not fully reliable when you wish to find out whether two tokens, e.g., macro tokens, trigger the same actions/expansion cascade.

E.g., with the example below the macros \macroA, \macroB, \macroC, \macroD do exactly the same. Nonetheless with \ifx-comparison they are considered different in case in their definitions' ⟨parameter texts⟩ different character tokens of category 6(parameter) are used for denoting the parameters:

\newlinechar=`\^^J %
% Let's use Z in the same way as #, i.e., for denoting parameter:
\catcode`\Z=6 %
% Let's define some macros which do exactly the same:
\def\macroA#1{In parentheses the result of processing the argument: (#1)}%
\def\macroB#1{In parentheses the result of processing the argument: (Z1)}%
\def\macroCZ1{In parentheses the result of processing the argument: (Z1)}%
\def\macroDZ1{In parentheses the result of processing the argument: (#1)}%
\def\space{ }%
% Reset Z to be a letter:
\catcode`\Z=11 %
\def\printmessagecompare#1#2{%
  \message{%
    ^^J%
    \string\ifx\string#1\string#2\space equal\string\else\space different\string\fi\space yields:
    \ifx#1#2 equal\else different\fi
  }%
}%
\message{^^JThis is the result of \string\macroA{Argument}: \macroA{Argument}}%
\message{^^JThis is the result of \string\macroB{Argument}: \macroB{Argument}}%
\message{^^JThis is the result of \string\macroC{Argument}: \macroC{Argument}}%
\message{^^JThis is the result of \string\macroC{Argument}: \macroC{Argument}}%
\printmessagecompare\macroA\macroA
\printmessagecompare\macroA\macroB
\printmessagecompare\macroA\macroC
\printmessagecompare\macroA\macroD
\printmessagecompare\macroB\macroA
\printmessagecompare\macroB\macroB
\printmessagecompare\macroB\macroC
\printmessagecompare\macroB\macroD
\printmessagecompare\macroC\macroA
\printmessagecompare\macroC\macroB
\printmessagecompare\macroC\macroC
\printmessagecompare\macroC\macroD
\printmessagecompare\macroD\macroA
\printmessagecompare\macroD\macroB
\printmessagecompare\macroD\macroC
\printmessagecompare\macroD\macroD
\csname stop\endcsname
\bye

You get the following messages on the terminal/console/shell:

This is the result of \macroA{Argument}: In parentheses the result of processin
g the argument: (Argument)

This is the result of \macroB{Argument}: In parentheses the result of processin
g the argument: (Argument)

This is the result of \macroC{Argument}: In parentheses the result of processin
g the argument: (Argument)

This is the result of \macroC{Argument}: In parentheses the result of processin
g the argument: (Argument)

\ifx\macroA\macroA equal\else different\fi yields:  equal

\ifx\macroA\macroB equal\else different\fi yields:  equal

\ifx\macroA\macroC equal\else different\fi yields: different

\ifx\macroA\macroD equal\else different\fi yields: different

\ifx\macroB\macroA equal\else different\fi yields:  equal

\ifx\macroB\macroB equal\else different\fi yields:  equal

\ifx\macroB\macroC equal\else different\fi yields: different

\ifx\macroB\macroD equal\else different\fi yields: different

\ifx\macroC\macroA equal\else different\fi yields: different

\ifx\macroC\macroB equal\else different\fi yields: different

\ifx\macroC\macroC equal\else different\fi yields:  equal

\ifx\macroC\macroD equal\else different\fi yields:  equal

\ifx\macroD\macroA equal\else different\fi yields: different

\ifx\macroD\macroB equal\else different\fi yields: different

\ifx\macroD\macroC equal\else different\fi yields:  equal

\ifx\macroD\macroD equal\else different\fi yields:  equal

E.g., macros not processing any arguments and doing exactly the same but not defined with the same constellation of prefixes like \long or \outer or \protected are considered different with \ifx-comparison:

\newlinechar=`\^^J %
% Let's define some macros which do exactly the same:
\def\macroA{Expansion of the macro.}%
\long\def\macroB{Expansion of the macro.}%
\def\space{ }%
\def\printmessagecompare#1#2{%
  \message{%
    ^^J%
    \string\ifx\string#1\string#2\space equal\string\else\space different\string\fi\space yields:
    \ifx#1#2 equal\else different\fi
  }%
}%
\message{^^JThis is the result of \string\macroA: \macroA}%
\message{^^JThis is the result of \string\macroB: \macroB}%
\printmessagecompare\macroA\macroA
\printmessagecompare\macroB\macroB
\printmessagecompare\macroA\macroB
\printmessagecompare\macroB\macroA
\csname stop\endcsname
\bye

You get the following messages on the terminal/console/shell:

This is the result of \macroA: Expansion of the macro.

This is the result of \macroB: Expansion of the macro.

\ifx\macroA\macroA equal\else different\fi yields:  equal

\ifx\macroB\macroB equal\else different\fi yields:  equal

\ifx\macroA\macroB equal\else different\fi yields: different

\ifx\macroB\macroA equal\else different\fi yields: different
0

The gist of this answer is what is provided in wipet's answer, where \futurelet is used.

However, a slight modification could be:

If expandability is not needed and you don't like to work with delimited macro arguments, where you need to specify an argument delimiter which serves as "sentinel token" for finding the last token of the result of "hitting" the first token of the argument with \expandafter, and which therefore must not occur in the result of "hitting" the first token of the argument with \expandafter, you can combine \futurelet with \afterassignment and stick to undelimited macro arguments:

\newcommand{\startswithmeaningofmymacro}[1]{%
  \begingroup
  \afterassignment\RemoveArgAndCompareArgsFirstTokensMeaning
  \expandafter\futurelet\expandafter\Scratchtoken\expandafter{#1}%
}%
\newcommand\RemoveArgAndCompareArgsFirstTokensMeaning[1]{%
  \expandafter\endgroup
  \ifx\Scratchtoken\mymacro TRUE\else FALSE\fi
}%

\documentclass{minimal}

%% As the test is on meanings of tokens, the following two definitions
%% affect the results of the test:
\def\mymacro#1{Does something with #1}
\let\mymacrocopy=\mymacro
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\def\mymacroinbraces{{\mymacro{1}}something}
\def\mymacrocopyfoo{\mymacrocopy{1}}
\def\mymacrocopybar{\mymacrocopy{2}}
\def\mymacrocopybaz{\mymacrocopy{3}}
\def\mymacrocopyqux{\mymacrocopy{4}\extra}
\def\mymacrosecond{\something\mymacrocopy{3}}
\def\mymacrocopyinbraces{{\mymacrocopy{1}}something}
\def\bat{{\mymacro}{1}}
\def\bau{{xx\fi}}
\def\bav{}
\def\baw{ }

\begin{document}

\ttfamily

\startswithmeaningofmymacro{\foo} - expected: TRUE

\startswithmeaningofmymacro{\bar} - expected: TRUE

\startswithmeaningofmymacro{\baz} - expected: TRUE

\startswithmeaningofmymacro{\qux} - expected: TRUE

\startswithmeaningofmymacro{\mymacrocopyfoo} - expected: TRUE

\startswithmeaningofmymacro{\mymacrocopybar} - expected: TRUE

\startswithmeaningofmymacro{\mymacrocopybaz} - expected: TRUE

\startswithmeaningofmymacro{\mymacrocopyqux} - expected: TRUE

\startswithmeaningofmymacro{\romannumeral0 \mymacro} - expected: TRUE

\startswithmeaningofmymacro{\romannumeral0 \mymacrocopy} - expected: TRUE

\startswithmeaningofmymacro{\csname mymacro\endcsname} - expected: TRUE

\startswithmeaningofmymacro{\csname mymacrocopy\endcsname} - expected: TRUE

\startswithmeaningofmymacro{\mymacrosecond} - expected: FALSE

\startswithmeaningofmymacro{\nomymacro} - expected: FALSE

\startswithmeaningofmymacro{\hiddenmymacro} - expected: FALSE

\startswithmeaningofmymacro{\mymacroinbraces} - expected: FALSE

\startswithmeaningofmymacro{\mymacrocopyinbraces} - expected: FALSE

\startswithmeaningofmymacro{{\foo}} - expected: FALSE

\startswithmeaningofmymacro{\bat} - expected: FALSE

\startswithmeaningofmymacro{\bau} - expected: FALSE

\startswithmeaningofmymacro{\bav} - expected: FALSE

\startswithmeaningofmymacro{\baw} - expected: FALSE

Of course this can be fooled in case the result of hitting the first
token of the argument of \verb|\startswithmeaningofmymacro|  with
\verb|\expandafter| yields an empty argument while \verb|\mymacro|
is let equal to \verb|}|:

\let\mymacro=}

\startswithmeaningofmymacro{\romannumeral0 } - expected: FALSE

\startswithmeaningofmymacro{} - expected: FALSE

So you might wish to separately crank out the case of emptiness.

\end{document}

Of course this can be fooled in case the result of hitting the first token of the argument of \startswithmeaningofmymacro with \expandafter yields an empty argument while \mymacro is let equal to }.

So you might wish to separately crank out the case of emptiness:

\newcommand{\startswithmeaningofmymacro}[1]{%
  \begingroup
  \afterassignment\RemoveArgAndCompareArgsFirstTokensMeaning
  \expandafter\futurelet\expandafter\Scratchtoken\expandafter{#1}%
}%
\newcommand\RemoveArgAndCompareArgsFirstTokensMeaning[1]{%
  \expandafter\expandafter\expandafter\endgroup
  \expandafter\ifx\expandafter\Scratchtoken\expandafter
  \mymacro\expandafter\ifcat\expandafter$%
  \detokenize{#1}$FALSE\else TRUE\fi\else FALSE\fi
}%

% In case you work on an engine where \detokenize is not available,
% use expl3's  \tl_if_empty:nTF{#1}{TRUE}{FALSE} or 
% \UD@CheckWhetherNull{#1}{TRUE}{FALSE} from my other answer 
% instead of  \ifcat$\detokenize{#1}$FALSE\else TRUE\fi.

\documentclass{minimal}

%% As the test is on meanings of tokens, the following two definitions
%% affect the results of the test:
\def\mymacro#1{Does something with #1}
\let\mymacrocopy=\mymacro
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\def\mymacroinbraces{{\mymacro{1}}something}
\def\mymacrocopyfoo{\mymacrocopy{1}}
\def\mymacrocopybar{\mymacrocopy{2}}
\def\mymacrocopybaz{\mymacrocopy{3}}
\def\mymacrocopyqux{\mymacrocopy{4}\extra}
\def\mymacrosecond{\something\mymacrocopy{3}}
\def\mymacrocopyinbraces{{\mymacrocopy{1}}something}
\def\bat{{\mymacro}{1}}
\def\bau{{xx\fi}}
\def\bav{}
\def\baw{ }

\begin{document}

\ttfamily

\startswithmeaningofmymacro{\foo} - expected: TRUE

\startswithmeaningofmymacro{\bar} - expected: TRUE

\startswithmeaningofmymacro{\baz} - expected: TRUE

\startswithmeaningofmymacro{\qux} - expected: TRUE

\startswithmeaningofmymacro{\mymacrocopyfoo} - expected: TRUE

\startswithmeaningofmymacro{\mymacrocopybar} - expected: TRUE

\startswithmeaningofmymacro{\mymacrocopybaz} - expected: TRUE

\startswithmeaningofmymacro{\mymacrocopyqux} - expected: TRUE

\startswithmeaningofmymacro{\romannumeral0 \mymacro} - expected: TRUE

\startswithmeaningofmymacro{\romannumeral0 \mymacrocopy} - expected: TRUE

\startswithmeaningofmymacro{\csname mymacro\endcsname} - expected: TRUE

\startswithmeaningofmymacro{\csname mymacrocopy\endcsname} - expected: TRUE

\startswithmeaningofmymacro{\mymacrosecond} - expected: FALSE

\startswithmeaningofmymacro{\nomymacro} - expected: FALSE

\startswithmeaningofmymacro{\hiddenmymacro} - expected: FALSE

\startswithmeaningofmymacro{\mymacroinbraces} - expected: FALSE

\startswithmeaningofmymacro{\mymacrocopyinbraces} - expected: FALSE

\startswithmeaningofmymacro{{\foo}} - expected: FALSE

\startswithmeaningofmymacro{\bat} - expected: FALSE

\startswithmeaningofmymacro{\bau} - expected: FALSE

\startswithmeaningofmymacro{\bav} - expected: FALSE

\startswithmeaningofmymacro{\baw} - expected: FALSE

%-----------------------------------------------------------------

\let\mymacro=}

\startswithmeaningofmymacro{\romannumeral0 } - expected: FALSE

\startswithmeaningofmymacro{} - expected: FALSE

\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .