2

I'm looking for something fairly similar to this problem, of defining macros to print pseudonyms for people, but I'm not sure how to modify it (or if it is actually the right approach for what I'm after).

What I'm hoping for is to have a macros that are identifiers for people like \AnGo

When issued as is it should print a pseudonym, so
\AnGo >> "Edwin Smith"

When issued with an option [sh] to print a short form
\AnGo[sh] >> "Ted"

Another option [real] to print the actual name...
\AnGo[real] >> "Antonio Gonzales"

What would be the best approach here?

4

This is a possible solution:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\definepseudonym}{mmmm}
 {% #1 = command, #2 = pseudonym, #3 = short name, #4 = real name
  \NewDocumentCommand{#1}{O{pseudonym}}
   {
    \str_case:nn { ##1 }
     {
      {pseudonym}{#2}
      {sh}{#3}
      {real}{#4}
     }
   }
 }
\ExplSyntaxOff

\definepseudonym{\AnGo}{Edwin Smith}{Ted}{Antonio Gonzalez}

\begin{document}

\AnGo[real] was known as \AnGo\ abbreviated in \AnGo[sh].

\end{document}

enter image description here

Here's an extensible solution. You can define as many attributes you wish, besides the pseudonym.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\definepseudonym}{mmm}
 {% #1 = command name, #2 = pseudonym, #3 = other names
  \cs_new:cpn { #1 } { \genericpseudonym{#1} }
  \prop_gclear_new:c { g_pseudonym_#1_prop }
  \prop_gset_from_keyval:cn { g_pseudonym_#1_prop } { #3 }
  \prop_gput:cnn { g_pseudonym_#1_prop } { pseudonym } { #2 }
 }
\NewDocumentCommand{\genericpseudonym}{mO{pseudonym}}
 {
  \pseudonym_get:nn { #1 } { #2 }
 }

\cs_new:Nn \pseudonym_get:nn
 {
  \prop_item:cn { g_pseudonym_#1_prop } { #2 }
 }
\ExplSyntaxOff

\definepseudonym{AnGo}{Edwin Smith}{
  sh=Ted,
  real=Antonio Gonzalez,
  birthplace=Somewhere,
}

\begin{document}

\AnGo[real] was known as \AnGo\ abbreviated in \AnGo[sh].
He was born in \AnGo[birthplace].

\end{document}

enter image description here

  • The second extensible version is great as the options can be optional. The only thing I liked about the first option was that the input was simpler (if I'm starting with a list of tab delimited names and turn it into a list with lines of \definepseudonym{}{}{}{} ...). A way to have it both ways? So I could do \definepseudonym{AnGo}{Edwin Smith}{Ted}{}{Somewhere} leaving blank those arguments I don't need/want to reveal. – jan Apr 29 at 13:55
  • (I know this won't do \newcommand{\newpseudonym}[5]{\definepseudonym{#1}{#2}{sh=#3,real=#4,birthplace=#5}} since \newcommand can't have more than one optional argument but I'm unfamiliar with the xparse syntax ...) – jan Apr 29 at 13:55
  • @jan I'd prefer the key-value syntax rather than multiplying the arguments, making it difficult to remember which is which. With key-value, the order is irrelevant. – egreg Apr 29 at 14:38
  • It's probably better ... I can see the point. Just one last question: What would speak against also defining the Pseudonym with the key-value syntax like pseudonym=Edwin Smith (so that only the command itself is mandatory)? – jan Apr 29 at 16:44
  • @jan No problem in doing that, of course. – egreg Apr 29 at 16:54
3

An approach which does without additional packages:

\documentclass[a4paper]{article}
\makeatletter
%%----------------------------------------------------------------------
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
%%----------------------------------------------------------------------
%% 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]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Check whether argument contains no exclamation mark which is not nested 
%% in braces:
%%......................................................................
%% \UD@CheckWhetherNoExclam{<Argument which is to be checked>}%
%%                         {<Tokens to be delivered in case that argument
%%                           contains no exclamation mark>}%
%%                         {<Tokens to be delivered in case that argument
%%                           contains exclamation mark>}%
%%
\newcommand\UD@GobbleToExclam{}\long\def\UD@GobbleToExclam#1!{}%
\newcommand\UD@CheckWhetherNoExclam[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToExclam#1!}%
}%
%%----------------------------------------------------------------------
%%  \@PseudonymFork grabs the first thing behind a
%%  a token-sequence of pattern  !!pseudonym!sh!real!
%%......................................................................
\newcommand\@PseudonymFork{}
\long\def\@PseudonymFork#1!!pseudonym!sh!real!#2#3!!!!{#2}%
%%----------------------------------------------------------------------
%% \PseudonymFork
%%......................................................................
\newcommand\PseudonymFork[5]{%
  \UD@CheckWhetherNoExclam{#1}{%
    \@PseudonymFork!#1!pseudonym!sh!real!{%
                                           \PseudonymForkError
                                           {No identifier in optional argument}%
                                           {#2}%
                                           \PseudonymFork{#2}{#2}{#3}{#4}{#5}%
                                         }%<-case #1 is empty/has no tokens.
                   !!#1!sh!real!{#3}%<-case #1 = pseudonym.
                   !!pseudonym!#1!real!{#4}%<-case #1 = sh.
                   !!pseudonym!sh!#1!{#5}%<-case #1 = real.
                   !!pseudonym!sh!real!{%
                                         \PseudonymForkError
                                         {Identifier `#1' unknown}%
                                         {#2}%
                                         \PseudonymFork{#2}{#2}{#3}{#4}{#5}%
                                       }%<-case #1 = something else without exclamation-mark.
                   !!!!%
  }{\PseudonymForkError{Identifier `#1' unknown}{#2}\PseudonymFork{#2}{#2}{#3}{#4}{#5}}%<-case #1 = something else with exclamation-mark.
}%
\newcommand\PseudonymForkError[2]{%
  \begingroup
  \def\UD@tempa{#1}%
  \@onelevel@sanitize\UD@tempa
  \@latex@error{%
    \string\PseudonymFork:\MessageBreak
    \UD@tempa\on@line,\MessageBreak
    thus default identifier `#2' used instead%
  }{%
    Either omit the optional argument or specify "sh" or "real"
    or "pseudonym".\MessageBreak
    Specifying "sh" yields the short form.\MessageBreak
    Specifying "real" yields the real name.\MessageBreak
    Specifying "pseudonym" yields the pseudonym.\MessageBreak
    Omitting the optional argument yields the \PseudonymFork{#2}{#2}{pseudonym}{short form}{real name}.%
  }%
  \endgroup
}%
%%----------------------------------------------------------------------
%% \DefinePseudonym{<macro name>}%
%%                 {<default>}%
%%                 {<pseudonym>}%
%%                 {<short form>}%
%%                 {<real name>}%
%%----------------------------------------------------------------------
\DeclareRobustCommand\DefinePseudonym[5]{%
  \@bsphack
  \UD@CheckWhetherNoExclam{#2}{%
     \@PseudonymFork
     !#2!pseudonym!sh!real!{\UD@firstoftwo}%
     !!#2!sh!real!{\UD@secondoftwo}%
     !!pseudonym!#2!real!{\UD@secondoftwo}%
     !!pseudonym!sh!#2!{\UD@secondoftwo}%
     !!pseudonym!sh!real!{\UD@firstoftwo}%
     !!!!%
  }{\UD@firstoftwo}%
  {\DefinePseudonymError{#2}}%
  {\@ifdefinable{#1}{\DeclareRobustCommand#1[1][#2]{\PseudonymFork{##1}{#2}{#3}{#4}{#5}}}}%
  \@esphack
}%
\newcommand\DefinePseudonymError[1]{%
  \begingroup
  \def\UD@tempa{#1}%
  \@onelevel@sanitize\UD@tempa
  \@latex@error{%
    \string\DefinePseudonym:\MessageBreak
    Default-identifier\UD@CheckWhetherNull{#1}{}{ `\UD@tempa'} incorrect\on@line!\MessageBreak
    Use one of the identifiers "pseudonym", "sh" or "real" instead%
  }{%
    Specify either "sh" or "real" or "pseudonym".\MessageBreak
    Specifying "sh" yields that the short form will be the default.\MessageBreak
    Specifying "real" yields that the real name will be the default.\MessageBreak
    Specifying "pseudonym" yields that the pseudonym will be the default.%
  }%
  \endgroup
}%
%%----------------------------------------------------------------------
\makeatother

\begin{document}

\noindent
\verb|\DefinePseudonym{|%
\textit{$\langle$macro name$\rangle$}%
\verb|}{|%
\textit{$\langle$default$\rangle$}%
\verb|}{|%
\textit{$\langle$pseudonym$\rangle$}%
\verb|}{|%
\textit{$\langle$short~form$\rangle$}%
\verb|}{|%
\textit{$\langle$real~name$\rangle$}%
\verb|}|
\bigskip     

\noindent
\verb|\DefinePseudonym{\AnGo}{pseudonym}{Edwin Smith}{Ted}{Antonio Gonzales}|
\DefinePseudonym{\AnGo}{pseudonym}{Edwin Smith}{Ted}{Antonio Gonzales}%

\noindent\hrulefill\null

\noindent
   % Error and default; default is pseudonym:
%\verb|\AnGo[unknown specifier]|: \AnGo[unknown specifier]\\
   % Error and default; default is pseudonym:
%\verb|\AnGo[]|: \AnGo[]\\
   % default; default is pseudonym:
\verb|\AnGo|: \AnGo\\
   % pseudonym:
\verb|\AnGo[pseudonym]|: \AnGo[pseudonym]\\
   % short form:
\verb|\AnGo[sh]|: \AnGo[sh]\\
   % real name:
\verb|\AnGo[real]|: \AnGo[real]

\end{document}

enter image description here

1

A slight variation of egreg's answer lets you specify at the time of calling \definepseudonym the default in case of not providing an optional argument:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\definepseudonym}{mO{pseudonym}mm}
 {% #1 = command name
  % #2 = default used in case of not providing the optional argument
  % #3 = pseudonym
  % #4 = other names
  \exp_args:Nc{\NewDocumentCommand}{#1}{O{#2}}
   {
    \prop_item:cn { g_pseudonym_#1_prop } { ##1 }
   }
  \prop_gclear_new:c { g_pseudonym_#1_prop }
  \prop_gset_from_keyval:cn { g_pseudonym_#1_prop } { #4 }
  \prop_gput:cnn { g_pseudonym_#1_prop } { pseudonym } { #3 }
 }
\ExplSyntaxOff

\definepseudonym{AnGo}[pseudonym]{Edwin Smith}{
  sh=Ted,
  real=Antonio Gonzalez,
  birthplace=Somewhere,
}

\begin{document}

\AnGo[real] was known as \AnGo[pseudonym] abbreviated in \AnGo[sh].
He was born in \AnGo[birthplace].

Applying \verb|\AnGo| without optional argument yields the pseudonym: \AnGo

This is because within \verb|\definepseudonym|'s second argument, which is 
optional, "pseudonym" was specified as the default which is to be used when
not providing an optional argument.

If instead of "pseudonym", e.g., "real" was specified here, then
\verb|\AnGo| without optional argument would yield the real
name/the value of the "real"-property.

\end{document}

Another variation where the "pseudonym"-property is not treated separately from other properties/other names:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\definepseudonym}{mO{pseudonym}m}
 {% #1 = command name
  % #2 = default used in case of not providing the optional argument
  % #3 = property-value-list
  \exp_args:Nc{\NewDocumentCommand}{#1}{O{#2}}
   {
    \prop_item:cn { g_pseudonym_#1_prop } { ##1 }
   }
  \prop_gclear_new:c { g_pseudonym_#1_prop }
  \prop_gset_from_keyval:cn { g_pseudonym_#1_prop } { #3 }
 }
\ExplSyntaxOff

\definepseudonym{AnGo}[pseudonym]{
  pseudonym=Edwin Smith,
  sh=Ted,
  real=Antonio Gonzalez,
  birthplace=Somewhere,
}

\begin{document}

\AnGo[real] was known as \AnGo[pseudonym] abbreviated in \AnGo[sh].
He was born in \AnGo[birthplace].

Applying \verb|\AnGo| without optional argument yields the pseudonym: \AnGo

This is because within \verb|\definepseudonym|'s second argument, which is 
optional, "pseudonym" was specified as the default which is to be used when
not providing an optional argument.

If instead of "pseudonym", e.g., "real" was specified here, then
\verb|\AnGo| without optional argument would yield the real
name/the value of the "real"-property.

\end{document}

Yet another variation could be letting you specify a global default-value for the optional argument of a pseudonym-macro which at the time of calling \definepseudonym can be overridden via also specifying the default-property within \definepseudonym's second argument:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\cs_generate_variant:Nn \prop_item:Nn {cx}
\tl_new:N {\jan_GlobalPseudonymDefault}
\tl_gset:Nn {\jan_GlobalPseudonymDefault} {pseudonym}
\NewDocumentCommand{\SetGlobalPseudonymDefault}{m}{\tl_gset:Nn {\jan_GlobalPseudonymDefault} {#1}}
\NewDocumentCommand{\definepseudonym}{mm}
 {% #1 = command name
  % #2 = property-value-list
  \exp_args:Nc{\NewDocumentCommand}{#1}{O{default}}
   {
    \prop_item:cn { g_pseudonym_#1_prop } { ##1 }
   }
  \prop_gclear_new:c { g_pseudonym_#1_prop }
  \prop_gset_from_keyval:cn { g_pseudonym_#1_prop } { #2 }
  \prop_if_in:cnTF{ g_pseudonym_#1_prop }{ default }{}{%
   \prop_gput:cnV { g_pseudonym_#1_prop } { default } { \jan_GlobalPseudonymDefault }
  }
  \prop_gput:cnx { g_pseudonym_#1_prop } { default }
  { \prop_item:cx { g_pseudonym_#1_prop } { \prop_item:cn { g_pseudonym_#1_prop } { default } } }
 }
\ExplSyntaxOff

% The global default is initialized to "peseudonym" but we change it
% to the real name:
\SetGlobalPseudonymDefault{real}

\definepseudonym{AnGo}{
  pseudonym=Edwin Smith,
  sh=Ted,
  real=Antonio Gonzalez,
  birthplace=Somewhere,
  % Override the global default:
  %default=sh,
  default=pseudonym,
}

\begin{document}

\AnGo[real] was known as \AnGo[pseudonym] abbreviated in \AnGo[sh].
He was born in \AnGo[birthplace].

Applying \verb|\AnGo| without optional argument yields the pseudonym: \AnGo

This is because within \verb|\definepseudonym|'s second argument, pseudonym
was specified as the value of the default-property.

If the default-property would not have been specified here, it would have
got the value stored in \verb|\jan_GlobalPseudonymDefault|, which in turn
can be changed globally via \verb|\SetGlobalPseudonymDefault|.

\end{document}
  • But wasn't this already how it was working in egreg's answer? – jan Apr 29 at 16:58
  • @jan Maybe I am overlooking something but if I got it right, with egreg's answer the default delivered in case of not providing \AnGo's optional argument is hardcoded as "pseudonym" in the parameter text of the definition of the \genericpseudonym-macro and thus cannot be specified differently at the time of calling \definepseudonym. – Ulrich Diez Apr 29 at 17:03
  • I tried with \NewDocumentCommand{\genericpseudonym}{mO{real}} and it did what it was supposed to. – jan Apr 29 at 17:06
  • @jan Sure. But this implies modification/redefinition of \genericpseudonym. You may wish at the same time to do something like \definepseudonym{AnGo}[pseudonym]{Edwin Smith}{sh=Ted, real=Antonio Gonzalez, birthplace=Somewhere,} ... \definepseudonym{Gambol}[sh]{John Doe}{sh=Johann Gambolputty, real=Johann Gambolputty de von Ausfern-schplenden-schlitter-crasscrenbon-..., birthplace=Monty-Python-Land,} ... The default-properties in the optional arguments differ... \AnGo (without optional argument) yields the pseudonym. \Gambol (without optional argument) yields the short form. – Ulrich Diez Apr 29 at 17:13
  • I see your point. But in that case wouldn't it be better to have something like \definepseudonym{AnGo}{pseudonym=Edwin Smith, sh=Ted, real=Antonio Gonzalez, birthplace=Somewhere, } and define globally which to make the default? – jan Apr 29 at 17:20

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.