5

I defined a macro \test as follows:

\usepackage{xstring}
\newcommand{\test}[1]{
    \StrGobbleRight{#1}{1}[\temp]
    \expandafter\newcommand\csname\temp\endcsname{Name: \temp}
}

It creates a new macro with the name of its parameter, and then store this name inside this new macro (I only create this macro to illustrate my question, not that I really need the functionality of this macro). Thus, with

\test{aaa}
\aaa

one would get Name: aaa. However, if there's a \test{bbb} after this, then \aaa becomes Name: bbb, since \aaa stores the macro\temp, not its value.

I wish to keep the value of aaa a constant. For this I tried to add \expandafter before \temp, but it didn't work. What is the proper way to do this?

(I know I can change the second \newcommand in this example to \edef to solve this. But this won't work if I'm using \newtheorem. What I really wish to know is how to get the value of \temp.)

Below is a MWE.

\documentclass{article}

\usepackage{xstring}
\newcommand{\test}[1]{
    \StrGobbleRight{#1}{0}[\temp]
    \expandafter\newcommand\csname\temp\endcsname{Name: \temp}
}

\begin{document}

\test{aaa}
\aaa

\test{bbb}
\aaa

\end{document}
7
  • By the way, if you want to do \newtheorem, why not simply doing \newcommand{\test}[1]{\newtheorem{#1}{Name: #1}}? – egreg Mar 7 at 10:12
  • @egreg Actually I am writing a class file, in which I need to detect if a string contains * at last and remove the * if it does. The resulted string is \temp, and is used in many places. It is a little bit hard for me do make a real MWE. – Jinwen Mar 7 at 10:16
  • Then ask the real question with some more details. – egreg Mar 7 at 10:30
  • @egreg See the description under this question. – Jinwen Mar 7 at 10:45
  • Why did you delete your other question? I was just about to post an answer that should've covered everything... :( – Skillmon Mar 7 at 11:36
6

You have to use \edef instead \def:

\def\test#1{%
   \def\temp{#1}% It emulates your \StrGobble
   \expandafter\edef\csname#1\endcsname{Name: \temp}
}

Or more straightforward:

\def\test#1{\expandafter\def\csname#1\endcsname{Name: #1}}

Edit Ok, when you are using \newtheorem then you have more work. (I never use such things as \newcommand or \newtheorem, only \def, \edef, \gdef, \xdef, so, my life with TeX is more simple).

You can use \edef\tempi and three \expandafters:

\def\test#1{%
    \def\temp{#1}%
    \edef\tempi{name: \temp}%
    \expandafter\newtheorem\expandafter\temp\expandafter{\tempi}%
}
5
  • Yes, I know about this, and I've stated it in my question. Perhaps I haven't chosen the proper example. Actually I'm using \temp inside a \newtheorem, and \edef doesn't seem very helpful in this case. – Jinwen Mar 7 at 9:08
  • I just edited my question to use \newtheorem instead of \newcommand. – Jinwen Mar 7 at 9:19
  • @Jinwen OK, see my edit. – wipet Mar 7 at 9:33
  • Thanks for this! May I ask what are these \expandafter for in \expandafter\newtheorem\expandafter\temp\expandafter{\tempi}? I have no idea when or where should I add it. – Jinwen Mar 7 at 9:37
  • You need to process \newtheorem{\temp}{expanded\tempi}, so you must to expand the \tempi by \expandafter. The \expandafters chain must skip all tokens which need not to be expanded before \newtheorem is processed. Moreower, \newtheorem accepts its first parameter as one token \temp, you need not use three tokens {\temp} here. So, you can spare two \expandafters in the chain. – wipet Mar 7 at 9:50
3

You can do it with xstring, but it's much simpler with expl3:

\documentclass{article}

\ExplSyntaxOn
\NewDocumentCommand{\test}{O{0}m}
 {
  \jinwen_test:nn { #1 } { #2 }
 }

\cs_new_protected:Nn \jinwen_test:nn
 {% #1 = how many characters to gobble from the right
  % #2 = string
  \cs_new:cpx { #2 } { Name: ~ \tl_range:nnn { #2 } { 1 } { - 1 - #1 } }
 }
\ExplSyntaxOff

\frenchspacing 

\begin{document}

\test{aaa}
\test[3]{abcdef}

\texttt{\meaning\aaa}

\texttt{\meaning\abcdef}

\end{document}

enter image description here

With xstring you can use an argument swapping technique in order to avoid \edef.

\documentclass{article}
\usepackage{xstring}

\makeatletter
\newcommand{\test}[2][0]{%
  \StrGobbleRight{#2}{#1}[\jinwen@temp]
  \expandafter\jinwen@makecommand\expandafter{\jinwen@temp}{#2}%
}
\newcommand{\jinwen@makecommand}[2]{%
  \expandafter\newcommand\csname#2\endcsname{Name: #1}%
}
\makeatother

\frenchspacing

\begin{document}

\test{aaa}
\test[3]{abcdef}

\texttt{\meaning\aaa}

\texttt{\meaning\abcdef}

\end{document}
2
  • Thanks for this. However I don't know how to fix my new added example in the question (the one with \newtheorem) according to your answer. Would you mind looking into it? – Jinwen Mar 7 at 9:27
  • 2
    @Jinwen You should never change a question so as to invalidate existing answers. Of course the answer to the modified question is just to do \newtheorem{#1}{Name #1}. Please, roll back. And you should never do \newtheorem{aaa}{...} and use \aaa\endaaa. – egreg Mar 7 at 9:52
2

Approaches where a temporary macro \temp is defined by \StrGobbleRight only work out as long as the string in question does not contain hashes (#).


You can have the toplevel-expansion of \temp as an argument of another macro which does the actual work:

\documentclass{article}

\usepackage{xstring}

\newcommand{\test}[1]{%
    \begingroup
    \StrGobbleRight{#1}{1}[\temp]%
    \expandafter\endgroup\expandafter\innertest\expandafter{\temp}%
}%
\newcommand\innertest[1]{%
  \expandafter\newcommand\csname#1\endcsname{Name: #1}%
}%


\begin{document}

\test{aaax}

\test{bbbx}

\texttt{\string\aaa:\meaning\aaa}

\texttt{\string\bbb:\meaning\bbb}

\end{document}

enter image description here


If ε-TeX-extensions are allowed you can use \protected@edef and wrap everything but \temp into \unexpanded—actually in this example \unexpanded is not needed as the characters Name: are not expandable, but there might be something like \newtheorem instead:

\documentclass{article}

\usepackage{xstring}

\makeatletter
\newcommand{\test}[1]{%
    \StrGobbleRight{#1}{1}[\temp]%
    \expandafter\@ifdefinable\csname\temp\endcsname{%
      \expandafter\protected@edef\csname\temp\endcsname{\unexpanded{Name: }\temp}%
    }%
}%
\makeatother

\begin{document}

\test{aaax}

\test{bbbx}

\texttt{\string\aaa:\meaning\aaa}

\texttt{\string\bbb:\meaning\bbb}

\end{document}

enter image description here


If you like it complicated you can apply \expandafter before \Exchangeing tokens.
You can use \romannumeral for keeping expansion going until a non-positive number is found.
(\romannumeral silently discards non-positive numbers without delivering any token in return.)

With the example below \Stopromannumeral denotes the number 0 in a way where you don't have unwanted removal of a trailing space token and where no explicit character-token 0 is involved which could probably be affected/changed by \uppercase/\lowercase.

The \expandafter-chain launched during the expansion of \csname launches \romannumeral-expansion.
\romannumeral in turn triggers expanding the two \expandafter behind the \romannumeral-token which in turn yields toplevel-expansion of \temp.
Then \romannumeral triggers \Exchange.
After that \Stopromannumeral, i.e., a TeX-⟨number⟩-quantity of value "0", is found which ends \romannumeral with \romannumeral discarding the tokens forming that ⟨number⟩-quantity without delivering tokens in return.

\documentclass{article}

\usepackage{xstring}

\makeatletter
\newcommand\Exchange[2]{#2#1}%
\@ifdefinable\Stopromannumeral{\chardef\Stopromannumeral=`\^^00}%
\makeatother


\newcommand{\test}[1]{%
    \StrGobbleRight{#1}{1}[\temp]%
    \expandafter\newcommand
    \csname\temp\expandafter\endcsname
                \expandafter{%
                  \romannumeral
                  \expandafter\Exchange\expandafter{\temp}{\Stopromannumeral Name: }%
                }%
}%


\begin{document}

\test{aaax}

\test{bbbx}

\texttt{\string\aaa:\meaning\aaa}

\texttt{\string\bbb:\meaning\bbb}

\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.