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}

Jinwen
  • 8,518
  • By the way, if you want to do \newtheorem, why not simply doing \newcommand{\test}[1]{\newtheorem{#1}{Name: #1}}? – egreg Mar 07 '21 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 07 '21 at 10:16
  • Then ask the real question with some more details. – egreg Mar 07 '21 at 10:30
  • @egreg See the description under this question. – Jinwen Mar 07 '21 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 07 '21 at 11:36
  • @Skillmon I'm writing a new one that mainly focus on the macro \CreateTheorem. Previously I'm asking things related to it, but was hard to read and to answer. – Jinwen Mar 07 '21 at 11:39
  • @Skillmon Please have a look at the new question: https://tex.stackexchange.com/q/586211/194994 – Jinwen Mar 07 '21 at 12:16

3 Answers3

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}%
}
wipet
  • 74,238
  • 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 07 '21 at 09:08
  • I just edited my question to use \newtheorem instead of \newcommand. – Jinwen Mar 07 '21 at 09:19
  • @Jinwen OK, see my edit. – wipet Mar 07 '21 at 09: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 07 '21 at 09: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 07 '21 at 09: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}

egreg
  • 1,121,712
  • 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 07 '21 at 09: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 07 '21 at 09: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

Ulrich Diez
  • 28,770