0

I wrote a command for accessing a hardcoded string ("For Reasons™", because xstring StrMid is incredibly slow on large strings) and wonder now

  • Why can the results be printed but not reused?
  • How would I write this command so that it can be further used?

This snippet runs as expected and generates the following pdf.

  debug for part two
  \newcounter{debugctr}
  \setcounter{debugctr}{-1}
    {\loop
    \stepcounter{debugctr}
    \edef\tmpinput{\the\numexpr \value{debugctr} - 0}
    \def\tmpchar{\expandafter\lucidcharat\tmpinput}
    (\tmpinput)[\tmpchar],
    %\edef\tmpcharr{\tmpchar}
    \show\tmpchar
    %\typeout{tmpchar is \tmpchar}
    \ifnum \value{debugctr}<3
    \repeat }

enter image description here

The command lucidcharat is defined as follows:

\newcommand{\lucidcharat}[1]{
\edef\tmpp{#1}
\IfEq{\tmpp}{0}{1}{}%
\IfEq{\tmpp}{1}{2}{}%
\IfEq{\tmpp}{2}{2}{}%
}

and is input to the other file with \input{sneakycode}.

I assume this all has to do with how fickle xstring is with regard to evaluation. To demonstrate what I mean with "results can not be further used" uncomment one of the commented lines above and you'll see that neither typeout nor edef work. They'll error out with

!  Undefined control sequence.
\reserved@a ->\def \xs@dessep
{,}\@xs@readdecimalpart
l.617 \repeat

Please help me understand how a command using IfEq internally would be written so that it can be reused e.g. for comparison of the output and why it is not working currently.

Here's the complete MCVE for copy-pasting again (It's the same as above, just put together into one file):

\documentclass{article} \usepackage[utf8]{inputenc}
\usepackage{xstring}

\newcommand{\lucidcharat}[1]{
\edef\tmpp{#1}
\IfEq{\tmpp}{0}{1}{}%
\IfEq{\tmpp}{1}{2}{}%
\IfEq{\tmpp}{2}{2}{}%
}

\begin{document}
  debug for part two
  \newcounter{debugctr}
  \setcounter{debugctr}{-1}
    {\loop
    \stepcounter{debugctr}
    \edef\tmpinput{\the\numexpr \value{debugctr} - 0}
    \def\tmpchar{\expandafter\lucidcharat\tmpinput}
    (\tmpinput)[\tmpchar],
    %\edef\tmpcharr{\tmpchar}
    \show\tmpchar
    %\typeout{tmpchar is \tmpchar}
    \ifnum \value{debugctr}<3
    \repeat }
\end{document}
5
  • 1
    please post your code in a form that allows people to run it and debug the issue, but note that unrelated to xstring you can not use fragile commands (anything using assignments internally) in edef typeout and similar contexts. Commented Sep 6, 2020 at 9:06
  • @DavidCarlisle Re your second comment: The argument is something that should evaluate to a number (a counter, a literal numeric character, the result of a numexpr, ...). You can see that in the first code snippet. But I don't think that edef is my problem? I mean, the command evaluates correctly when writing the result to the PDF
    – lucidbrot
    Commented Sep 6, 2020 at 9:41
  • 1
    I don't know what answer you want other than \lucidacharat is a fragile command (because of the def that you do and xstring does) so can not be used in a \typeout unless prefixed by \protect when it will just print as itself. You can probably do expandable tests instead of using xstring but you have not said what test you are intending. Commented Sep 6, 2020 at 9:58
  • @DavidCarlisle I'm looking for an explanation why that does not work. (I have to retract my previous statement that edef is not my problem. Using ifnum instead of IfEq does not help.) I guess it boils down to me not understanding fragility. Why is the def a problem?
    – lucidbrot
    Commented Sep 6, 2020 at 10:13
  • because in an edef (or a write) assignments do not happen, just expansion, so if you expand \def\foo{} then \def stays as the token \def and \foo gives an undefined command error as you are expanding it when it is not defined. Commented Sep 6, 2020 at 10:16

1 Answer 1

3

It is not clear what is your intended output, but as you need it to work in an edef, you need expandable tests, this uses \ifcase to test for the argument being 0 1 or 2 and outputs 1 2 2 respectively.

\documentclass{article} \usepackage[utf8]{inputenc}


\newcommand{\lucidcharat}[1]{%
\ifcase\numexpr#1\relax
1\or2\or2\else\fi}

\begin{document}
  debug for part two
  \newcounter{debugctr}
  \setcounter{debugctr}{-1}
    {\loop
    \stepcounter{debugctr}%
    \edef\tmpinput{\the\numexpr \value{debugctr} - 0}%
    \def\tmpchar{\expandafter\lucidcharat\tmpinput}%
    (\tmpinput)[\tmpchar],
    \edef\tmpcharr{\tmpchar}%
    \show\tmpcharr
    \typeout{tmpchar is \tmpchar}%
    \ifnum \value{debugctr}<3
    \repeat }
\end{document}
6
  • This is exactly what I was hoping to do. But I'm still wondering why the edef in the command definition posed a problem. I don't have any def inside the argument to lucidcharat, have I? Because that's how I understand your comment above - that I cannot \edef\tempp{#1} when #1 contains a \def
    – lucidbrot
    Commented Sep 6, 2020 at 10:25
  • 1
    yes but you can't have lucidacharat in an edef for the same reason it expanded to an edef. @lucidbrot Commented Sep 6, 2020 at 10:52
  • 1
    if you put lucidacharat in an edef (with any argument) after one expansion you had \edef\tmpp{...} then \edef expands to itself, and \tmpp gives a command not defined error. @lucidbrot Commented Sep 6, 2020 at 10:55
  • Oh, I see. Thanks! Why is the inner edef not be evaluated including its argument though?
    – lucidbrot
    Commented Sep 6, 2020 at 19:04
  • 1
    @lucidbrot because that's what happens in an edef or write, only expansion, no assignments Commented Sep 6, 2020 at 19:42

This site is temporarily in read-only mode and not accepting new answers.

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