3

I want to define a new command that does nothing if the argument is empty and does something is the argument is not empty. I want to define it this way because it will always be inside a second command but this second command will not always give an argument to the first command.

My idea is that when I write \reason{input}, if input is nothing (literally empty: \reason{}) then \reason does nothing, else (this is: input = whatever you like to write, including text and inline math mode) \reason does exactly the same as if it were \textnormal{(input)}.

My attempt was

\documentclass{article}

\newcommand{\reason}[1][]{\if\#1\ \ \else \textnormal{(#1)}\fi}

\begin{document}
\begin{enumerate}
    \item \reason{text $x$}
    \item \reason{ }
    \item \reason{\ }
    \item \reason{}
\end{enumerate}
\begin{itemize}


\end{document}

and I want to get

  1. (text $x$)
  2. ( )
  3. ( )

(I do not know how to compile rightly here so in 1. there should be a mathematical x and in 4. there should be no character but I wrote an space character just to have it nicely align in this website)

but I get

  1. (text $x$)
  2. ( )
  3. ( )
  4. ()

which is incorrect for 4. because there was no argument so nothing should be displayed and is correct for 1., 2. and 3. because there was an argument and therefore parenthesis should always be there.

I think that the main problem here is what is inside of \if...\else...\if, though I also do not know if it is possible to define a new command with conditionals, but as when I compiled it, there was no error, I think I can do that.

I would also like to set an empty default value.

Could you please help me to define well my command?

Thanks

0

3 Answers 3

2

First, your function is not employing an optional argument, so get rid of it. Second, use \ifx rather than \if, since you could have a non-blank argument that expands to nothing, and (I think) you do not want that to take the empty branch. Finally, I \detokenize the argument which takes care of other things, for example if the argument itself were \relax.

If you want default behavior for the empty-argument case, add the code just prior to the \else in the definition.

\documentclass{article}
\newcommand{\reason}[1]{\expandafter\ifx\expandafter\relax
  \detokenize{#1}\relax\else\textnormal{(#1)}\fi}
\begin{document}
\begin{enumerate}
    \item \reason{text $x$}
    \item \reason{ }
    \item \reason{\ }
    \item \reason{}
\end{enumerate}
\end{document}

enter image description here

Here's another way to implement the Default answer for an empty input:

\documentclass{article}
\newcommand{\reason}[2][Default answer]{\expandafter\ifx\expandafter\relax
  \detokenize{#2}\relax#1\else\textnormal{(#2)}\fi}
\begin{document}
\begin{enumerate}
    \item \reason{text $x$}
    \item \reason{ }
    \item \reason{\ }
    \item \reason{}
\end{enumerate}
\end{document}
1

With the defintion

\newcommand{\reason}[1][]{\if\#1\ \ \else \textnormal{(#1)}\fi}

you are defining \reason to take an optional argument, due to [] after [1]. You seem to want an argument in braces, so first of all you have to remove [].

However, this is not sufficient. Your code \if\#1 compares \# with 1. Since the former is a control sequence (TeXnically, a \chardef token) and the latter is a character, \if decides that the test returns false, so you get \textnormal{(#1)} no matter what.

How do you test whether an argument is empty? The easiest way is to use \detokenize.

\newcommand{\reason}[1]{\if\relax\detokenize{#1}\relax\else\textnormal{(#1)}\fi}

If the argument is not empty (and a space counts as not empty), \if will compare \relax with the first token of the stringified #1, returning false; if the argument is empty, \if compares \relax with \relax.

You could als use expl3:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\reason}{m}
 {
  \tl_if_empty:nF { #1 } { \textnormal{(#1)} }
 }
\ExplSyntaxOff

\begin{document}
\begin{enumerate}
\item \reason{text $x$}

\item \reason{ }

\item \reason{\ }

\item \reason{}
\end{enumerate}

\end{document}

enter image description here

If you change \tl_if_empty:nF into \tl_if_blank:nF, item 2 would result in nothing.

1

Your test doesn't work as expected because \if doesn't work the way you think. \if expands the tokens in front of it until it has two unexpandable ones, then it compares their character codes. More precisely, let's quote the TeXbook (p. 209):

TeX will expand macros following \if until two unexpandable tokens are found. If either token is a control sequence, TeX considers it to have character code 256 and category code 16, unless the current equivalent of that control sequence has been \let equal to a non-active character token. In this way, each token specifies a (character code, category code) pair. The condition is true if the character codes are equal, independent of the category codes. For example, after \def\a{*} and \let\b=* and \def\c{/}, the tests \if*\a and \if\a\b will be true, but \if\a\c will be false. Also \if\a\par will be false, but \if\par\let will be true.

Now, let's analyse how it works in your macro (Steven B. Segletes and egreg already pointed out that you don't use proper syntax for the optional argument [namely, square brackets when the macro is used], I won't comment on that further):

\newcommand{\reason}[1][]{\if\#1\ \ \else \textnormal{(#1)}\fi}

When this definition is read by TeX, it will store as the replacement text:

  • the control sequence tokens \if and \#;

  • a character token with character code 49 (the TeX-internal code for 1) and category code 12 (other);

  • two control sequence tokens \<space> (backslash followed by a space), i.e. control spaces;

  • the control sequence token \else;

  • the control sequence token \textnormal;

  • etc.

This is very important because you probably believed that the first #1 would be replaced by the first argument when \reason is expanded, however it won't. The first # has been tokenized differently, as we just saw (it is the name of the control sequence token \#, and as such is “embedded” in this control sequence token, so to speak).

Now, how does it behave according to the rules given in the TeXbook excerpt quoted above for \if?

  1. \if expands tokens until it has two unexpandable ones. It starts with \#, which is defined as \chardef\#=`\# in the LaTeX format (latex.ltx line 610 here). So, \# is a \chardef token, and as such is an unexpandable control sequence token. For the sake of \if, it has character code 256 (in traditional TeX) according to the rules quoted above, because a \chardef token is not \let-equivalent to a character token (these are just different kinds of beasts).

  2. \if needs another unexpandable token to decide. What comes next? The character token 1 (character code 49, category code 12). This character token is non-active (its catcode is different from 13), thus it is unexpandable. So, there we are, we now have two unexpandable tokens for \if.

  3. The first of these tokens is considered for \if to have character code 256, and the second one has character code 49 (this is the TeX internal code for 1, which normally coincides with ASCII).

  4. 256 is different from 49, therefore the \if test is false. As you can see, the true-or-false result of this test doesn't depend at all on the arguments passed to the \reason macro! (the expansion of the \if ... \fi construct depends on the first argument because of the other #1, but that is another thing).

Here are two ways to implement your macro, one using \if and \detokenize, the other using etoolbox's \ifstempty macro (the \detokenize e-TeX primitive expands to character tokens of category code 12, except spaces which come out with category code 10; the expansion of \detokenize{...} is empty if the ... “argument” is empty).

\documentclass{article}

\newcommand{\reason}[1]{%
  \if\relax\detokenize{#1}\relax
    % nothing
  \else
    \textnormal{(#1)}%
  \fi
}

\begin{document}

\begin{enumerate}
    \item \reason{text $x$}
    \item \reason{ }
    \item \reason{\ }
    \item \reason{}
\end{enumerate}

\end{document}

enter image description here

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\reason}[1]{%
  \ifstrempty{#1}{}{\textnormal{(#1)}}%
}

\begin{document}

\begin{enumerate}
    \item \reason{text $x$}
    \item \reason{ }
    \item \reason{\ }
    \item \reason{}
\end{enumerate}

\end{document}

Same output.

Note that in case you want to consider “only-spaces” the same way as “empty,” etoolbox has the \ifblank macro readily available.

If you want to obtain an error when the argument of your \reason macro contains blank lines (or, equivalently, \par tokens), use \newcommand* instead of \newcommand.

You must log in to answer this question.

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