5

In the process of learning how to use \csname, I'm trying to create a macro that allows one to specify integers as arguments and then will yield some content specific to the arguments, like this:

\DoContent{1}{3}

My current full-example attempt:

\documentclass[10pt]{article}

\usepackage{xifthen}

%End-Content output 
\newcommand{\OneOne}{One One content}
\newcommand{\OneTwo}{One Two content}
\newcommand{\OneThree}{One Three content}
\newcommand{\OneFour}{One Four content}
\newcommand{\OneFive}{One Five content}
\newcommand{\TwoOne}{Two One content}
\newcommand{\TwoTwo}{Two Two content}
% and so on....

%Translator "Switch" statement
\newcommand{\GetNumberText}[1]
{ 
\ifthenelse{\equal{#1}{1}}{One}{}
\ifthenelse{\equal{#1}{2}}{Two}{}
\ifthenelse{\equal{#1}{3}}{Three}{}
\ifthenelse{\equal{#1}{4}}{Four}{}
\ifthenelse{\equal{#1}{5}}{Five}{}
}

%csname macro to assemble the content-containing macro
\newcommand{\AssembleNumCmd}[2]{\csname #1#2\endcsname}

%this does not compile: desired functionality
%\newcommand{\DoContent}[2]{\AssembleNumCmd{\GetNumberText{#1}}{\GetNumberText{#2}}}

%this compiles: literal example
\newcommand{\DoContent}[2]{\AssembleNumCmd{One}{Three}}

\begin{document}

\DoContent{1}{3}

\end{document}

This should produce

One Three content

It appears that I can't use any conditional statements inside of the definition of\GetNumberText and still pass \GetNumberText{1} to \AssembleNumCmd. The document doesn't compile if I use the version of \DoContent that I have commented there (which is what I want to do).

Obviously, this is some somewhat silly gymnastics, and I'm open to better overall approaches. But, I still need want to know why this relatively simple method isn't working, and what to do to fix it.

I've tried using \protect here and there, and using a different "switch" macro that uses \ifx instead of \ifthenelse, among other smaller changes, and nothing seems to compile without error (" Missing \endcsname inserted", and so forth).

Is this a problem with my use of \csname? Or am I just being naive about macro expansion and fragility?

2
  • You are trying to use \ifthenelse inside a \csname. This won't work, \ifthenelse is not expandable. Oct 21, 2015 at 18:11
  • As a side remark, be careful with all the extra spaces in your \GetNumberText. There should be a bunch of %'s. Beware that in LaTeX3 code à la @egreg, \ExplSyntaxOn makes spaces in the source completely disappear (they must be input explicitely as ~). Even with an expandable switch which would work in a \csname.. you have to be careful with spaces, as the so-built name may well end up containing such probably unintended spaces.
    – user4686
    Oct 21, 2015 at 20:05

3 Answers 3

2

Here is a expl3 version, storing One, .... , Ten in a \clist variable:

\documentclass[10pt]{article}

\usepackage{xparse}

\newcommand{\OneOne}{One One content}
\newcommand{\OneTwo}{One Two content}
\newcommand{\OneThree}{One Three content}
\newcommand{\OneFour}{One Four content}
\newcommand{\OneFive}{One Five content}
\newcommand{\TwoOne}{Two One content}
\newcommand{\TwoTwo}{Two Two content}
% and so on....

%Translator "Switch" statement



\ExplSyntaxOn
\clist_new:N \g_turanc_numbername_clist
\clist_set:Nn \g_turanc_numbername_clist {One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten}

\NewDocumentCommand{\GetNumberText}{m}{%
  \clist_item:Nn \g_turanc_numbername_clist {#1}
}
\ExplSyntaxOff



%csname macro to assemble the content-containing macro
\newcommand{\AssembleNumCmd}[2]{\csname #1#2\endcsname}


\newcommand{\DoContent}[2]{\AssembleNumCmd{\GetNumberText{#1}}{\GetNumberText{#2}}}


\begin{document}

\DoContent{1}{3}

\DoContent{2}{2}

\DoContent{1}{5}

\end{document}

enter image description here

Old Version

I replaced the \ifthenelse conditional by a 'simpler' \ifcase ...\or ...\fi statement for the individual cases. I think it might work with other packages as well.

\documentclass[10pt]{article}

\usepackage{xifthen}

%End-Content output 
\newcommand{\OneOne}{One One content}
\newcommand{\OneTwo}{One Two content}
\newcommand{\OneThree}{One Three content}
\newcommand{\OneFour}{One Four content}
\newcommand{\OneFive}{One Five content}
\newcommand{\TwoOne}{Two One content}
\newcommand{\TwoTwo}{Two Two content}
% and so on....

%Translator "Switch" statement

\newcommand{\GetNumberText}[1]
{%
  \ifcase #1 
  \or
  One%
  \or
  Two%
  \or
  Three%
  \or
  Four%
  \or
  Five%
  \fi
}


%csname macro to assemble the content-containing macro
\newcommand{\AssembleNumCmd}[2]{\csname #1#2\endcsname}

%This does not compile: desired functionality
\newcommand{\DoContent}[2]{\AssembleNumCmd{\GetNumberText{#1}}{\GetNumberText{#2}}}


%this compiles: literal example
%\newcommand{\DoContent}[2]{\AssembleNumCmd{One}{Three}}

\begin{document}

\DoContent{1}{3}


\DoContent{2}{2}

\DoContent{1}{5}


\end{document}

**\ifx version:

There mustn't be white space characters so be careful when testing etc:

\documentclass[10pt]{article}


%End-Content output 
\newcommand{\OneOne}{One One content}
\newcommand{\OneTwo}{One Two content}
\newcommand{\OneThree}{One Three content}
\newcommand{\OneFour}{One Four content}
\newcommand{\OneFive}{One Five content}
\newcommand{\TwoOne}{Two One content}
\newcommand{\TwoTwo}{Two Two content}
% and so on....

%Translator "Switch" statement


\newcommand{\GetNumberText}[1]
{%
  \ifx#11%
  One%
  \else
  \ifx#12%
  Two%
  \else
  \ifx#13%
  Three%
  \else
  \ifx#14%
  Four%
  \else
  \ifx#15%
  Five%
  \fi
  \fi
  \fi
  \fi
  \fi
}



%csname macro to assemble the content-containing macro
\newcommand{\AssembleNumCmd}[2]{%
\csname #1#2\endcsname%
}

\newcommand{\DoContent}[2]{\AssembleNumCmd{\GetNumberText{#1}}{\GetNumberText{#2}}}

\begin{document}

\DoContent{1}{3}

\DoContent{2}{2}

\DoContent{1}{2}

\DoContent{1}{5}

\end{document}
11
  • I would never use ifcase or long if-else-test for this sort of things. Difficult to extend and debug and maintain. Oct 21, 2015 at 18:21
  • @UlrikeFischer: Yes, it's not nice, but it works ;-)
    – user31729
    Oct 21, 2015 at 18:27
  • Thanks for this. I wasn't even aware of \ifcase. But why does this work and not a set of \ifx statements? I tried the latter and it had the same problem as using \ifthenelse. Wouldn't \ifx be just as primitive as \ifcase?
    – turanc
    Oct 21, 2015 at 18:48
  • @turanc: It should work using \ifx but I don't know how you did your code actually
    – user31729
    Oct 21, 2015 at 18:52
  • @turanc: See also my update with the expl3 version
    – user31729
    Oct 21, 2015 at 18:56
5

As I already wrote in the comment, using \ifthenelse in \csname won't work.

Beside this you are overcomplicating things a lot. You already know that command names can contains other things then letters, so then why don't do use this knowledge for the content definitions?

\documentclass[10pt]{article}

\makeatletter
\@namedef{text1-1}{One One content}
\@namedef{test1-2}{One Two content}
\makeatother


\newcommand{\DoContent}[2]{\csname test#1-#2\endcsname}

\begin{document}

\DoContent{1}{2}

\end{document}

enter image description here

5
  • Why not \@nameuse{test#1-#2} then instead of \csname ... \endcsname?
    – user31729
    Oct 21, 2015 at 18:33
  • @ChristianHupfer: tradition ... Oct 21, 2015 at 18:39
  • Aha ... well, I improved my solution to be more maintainable and easier to debug ;-)
    – user31729
    Oct 21, 2015 at 18:44
  • Thanks for this response. I didn't know about \@namedef. I marked the next response as answered, though, since it was closer to answering the original question. Gave this an upvote, however.
    – turanc
    Oct 21, 2015 at 23:07
  • \@namedef is only a wrapper for \expandafter\def\csname ...\endcsname{ ...} . And I didn't follow your route with the string comparision as I think it is inefficient: it is quite unnecessary to compare the actual string with all lot of other strings all the time only to get its content text. Oct 22, 2015 at 7:13
1

As Ulrike pointed out, \ifthenelse cannot be used in \csname...\endcsname, where only macros fully expandable to characters are allowed.

Here's a different implementation using a case switch:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\DoContent}{mm}
 {
  \str_case:nnF { #1-#2 }
   {
    {1-1}{One~One~content}
    {1-2}{One~Two~content}
    {1-3}{One~Three~content}
    {1-4}{One~Four~content}
    {1-5}{One~Five~content}
    {2-1}{Two~One~content}
    {2-2}{Two~Two~content}
   }
   {Invalid~choice}
 }
\ExplSyntaxOff

\begin{document}

\DoContent{1}{3}

\end{document}

A possibly better interface for adding cases:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\DoContent}{mm}
 {
  \str_case:nVF { #1-#2 } \g_turanc_choices_tl 
   {Invalid ~ choice}
 }

\NewDocumentCommand{\AddContent}{m}
 {
  \clist_map_inline:nn { #1 }
   {
    \tl_gput_left:Nn \g_turanc_choices_tl { ##1 }
   }
 }

\tl_new:N \g_turanc_choices_tl

\ExplSyntaxOff

\AddContent{
  {1-1}{One One content},
  {1-2}{One Two content},
  {1-3}{One Three content},
  {1-4}{One Four content},
  {1-5}{One Five content},
}

\AddContent{
  {2-1}{Two One content},
}
\AddContent{
  {2-2}{Two Two content},
}

\begin{document}

\DoContent{1}{3}

\DoContent{2}{2}

\DoContent{9}{9}

\end{document}

Note that \AddContent is cumulative, so pairs can be added even after \begin{document}, wherever they become necessary. If you add a pair twice, the one in the last \AddContent wins, so you can later override a choice made at startup.

enter image description here

0

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.