16

This must be a LateX 101 question, so I ask for your kindness

I want to make a choice based on the value of a theorem counter.

I use the xifthen package (especially the \isin function, which tests if a first string is a part of a second string) and the following function:

\newcommand{\test}[2]{\ifthenelse{\isin{|#1|}{#2}}{Yes}{No}}

For example:

\test{17}{|15|17|19|} returns Yes

\test{18}{|15|17|19|} returns No

But how to replace the first argument of \test by the current value of a counter?

9

Or use package xstring, which has all manner of neat tests built in:

\documentclass{article}

\usepackage{xstring}

\newcounter{sausage}
\setcounter{sausage}{18}

\begin{document}

\IfSubStr{|17|18|19|}{|\arabic{sausage}|}{true}{false}
\IfSubStr{|17|15|19|}{|\arabic{sausage}|}{true}{false}

\end{document}
  • 1
    \IfSubStr{17|118|19} {\arabic{sausage}}{true}{false} yields true, which does not to seem to match the OP's request. You should write something like \IfSubStr{|17|118|19|} {|\arabic{sausage}|}{true}{false} instead. – Andrew Swann Nov 9 '16 at 8:47
12
\documentclass{article}
\usepackage{xifthen}
\newcounter{mycounter}
\setcounter{mycounter}{17}
\newcommand{\test}[2]{\ifthenelse{\isin{|#1|}{#2}}{Yes}{No}}
\begin{document}
\test{17}{|15|17|19|} returns Yes

\test{18}{|15|17|19|} returns No

\edef\tmp{\themycounter}
\expandafter\test\expandafter{\tmp}{|15|17|19|} returns Yes
\end{document}

This approach can be directly incorporated into the macro:

\documentclass{article}
\usepackage{xifthen}
\newcounter{mycounter}
\setcounter{mycounter}{17}
\newcommand{\test}[2]{%
  \edef\tmp{#1}%
  \ifthenelse{\expandafter\isin\expandafter{\expandafter|\tmp|}{#2}}{Yes}{No}%
}
\begin{document}
\test{17}{|15|17|19|} returns Yes

\test{18}{|15|17|19|} returns No

\test{\themycounter}{|15|17|19|} returns Yes
\end{document}

And for a completely different approach, here I use the listofitems package to parse argument #2 using expanded argument |#1| as the separator. If I get more than 1 element in the resulting array list, then one may conclude that the separator was found in #2.

\documentclass{article}
\usepackage{listofitems}
\newcounter{mycounter}
\newcommand{\test}[2]{%
  \edef\tmp{#1}%
  \expandafter\setsepchar\expandafter{\expandafter{\expandafter|\tmp|}}%
  \readlist\tmparray{#2}
  \ifnum\tmparraylen>1\relax Yes\else No\fi%
}
\begin{document}
\test{17}{|15|17|19|} returns Yes

\test{18}{|15|17|19|} returns No

\setcounter{mycounter}{17}
\test{\themycounter}{|15|17|19|} returns Yes
\end{document}
10

You can use pdfTeX's \pdfmatch{<strA>}{<strB>} which returns 1 if <strA> is found in <strB>. Pattern matching with punctuation requires some care. This is expandable:

enter image description here

\documentclass{article}

\newcommand{\test}[2]{\ifnum\pdfmatch{[|]#1[|]}{#2}=1 Yes\else No\fi}

\begin{document}

\verb!\test{17}{|15|17|19|}! returns: \test{17}{|15|17|19|}

\verb!\test{18}{|15|17|19|}! returns: \test{18}{|15|17|19|}

\newcounter{test}

\setcounter{test}{17}%
\verb!\test{\thetest}{|15|17|19|}! returns: \test{\thetest}{|15|17|19|}

\setcounter{test}{18}%
\verb!\test{\thetest}{|15|17|19|}! returns: \test{\thetest}{|15|17|19|}

\end{document}

The above pattern matching for \thetest works because \thetest expands to \arabic{test} (by default). If this is not the case, you can use \arabic{test} directly.

8

You can use \int_case:nnF of expl3.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\test}{m}
 {
  \int_case:nnF { #1 }
   {
    {15}{Yes}
    {17}{Yes,~it's~17}
    {19}{Yes,~but~it's~big}
   }
   {No}
 }
\ExplSyntaxOff

\newcommand{\fifteen}{15}
\newcounter{test}

\begin{document}

\test{17}

\test{\fifteen}

\setcounter{test}{19}
\test{\value{test}}

\setcounter{test}{42}
\test{\value{test}}

\test{43}

\end{document}

enter image description here

If you just want to do a yes/no comparison with a list of integers, you can do

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\test}{m}
 {
  \bool_if:nTF
   {
    \int_compare_p:n { #1 = 15 }
    ||
    \int_compare_p:n { #1 = 17 }
    ||
    \int_compare_p:n { #1 = 19 }
   }
   {Yes}
   {No}
 }
\ExplSyntaxOff

\newcommand{\fifteen}{15}
\newcounter{test}

\begin{document}

\test{17}

\test{\fifteen}

\setcounter{test}{19}
\test{\value{test}}

\setcounter{test}{42}
\test{\value{test}}

\test{43}

\end{document}

An extended version with three possible choices. The first does not accept the second argument in the form of alternatives, but is fully expandable; the second and the third versions accept the second argument, but only the latter is fully expandable.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\testA}{m}
 {
  \bool_if:nTF
   {
    \int_compare_p:n { #1 = 15 }
    ||
    \int_compare_p:n { #1 = 17 }
    ||
    \int_compare_p:n { #1 = 19 }
   }
   {Yes}
   {No}
 }

\NewDocumentCommand{\testB}{mm}
 {
  \seq_set_split:Nnn \l_tmpa_seq { | } { #2 }
  \seq_if_in:NfTF \l_tmpa_seq { \int_eval:n { #1 } }
   {Yes}
   {No}
 }
\cs_generate_variant:Nn \seq_if_in:NnTF { Nf }

\DeclareExpandableDocumentCommand{\testC}{mm}
 {
  \ferrard_test:fn { \int_eval:n { #1 } } { #2 }
 }

\cs_new:Nn \ferrard_test:nn
 {
  \ferrard_test_aux:nw { #1 } #2 | \q_stop
 }
\cs_new:Npn \ferrard_test_aux:nw #1 #2 | #3 \q_stop
 {
  \tl_if_empty:nTF { #2 }
   {No}
   {
    \int_compare:nTF { #1 = #2 }
     {Yes}
     { \ferrard_test_aux:nw { #1 } #3 | \q_stop }
   }
 }
\cs_generate_variant:Nn \ferrard_test:nn { f }
\ExplSyntaxOff

\newcommand{\fifteen}{15}
\newcounter{test}

\begin{document}

\section{Test A}

\testA{17}

\testA{\fifteen}

\setcounter{test}{19}
\testA{\value{test}}

\setcounter{test}{42}
\testA{\value{test}}

\testA{43}

\section{Test B}

\testB{17}{15|17|19}

\testB{\fifteen}{15|17|19}

\setcounter{test}{19}
\testB{\value{test}}{15|17|19}

\setcounter{test}{42}
\testB{\value{test}}{15|17|19}

\testB{43}{15|17|19}

\section{Test C}

\testC{17}{15|17|19}

\testC{\fifteen}{15|17|19}

\setcounter{test}{19}
\testC{\value{test}}{15|17|19}

\setcounter{test}{42}
\testC{\value{test}}{15|17|19}

\testC{43}{15|17|19}

\end{document}

enter image description here

0

A no package, easily extendable approach.

I used \numexpr only for allowing expressions in input. And I use \detokenize only for displaying input. Else, these e-TeX extensions are not needed.

\documentclass{article}

\makeatletter
% borrowed from xintkernel.sty
\long\def\dothis #1#2\orthat #3{\fi #1}%
\let\orthat \@firstofone
\makeatother

\newcommand*{\test}[1]{The test of \texttt{\detokenize{#1}} gives:
  \ifnum\numexpr#1=15 \dothis{Yes (15)}\fi
  \ifnum\numexpr#1=17 \dothis{Yes (17)}\fi
  \ifnum\numexpr#1=19 \dothis{Yes (19)}\fi
  \orthat{NO!}%
}%

\newcommand{\fifteen}{15}

\newcounter{mycounter}

\begin{document}

\test{17}

\test{\fifteen}

\setcounter{mycounter}{19}
\test{\value{mycounter}}

\setcounter{mycounter}{42}
\test{\value{mycounter}}

\test{43}

\test{11+4+4}

\end{document}

enter image description here

Here I do not handle presenting the set of allowed values in the form |15|17|19| but macros can be of course produced for that. And with , as separator, then this is ready-made (expandably) via xinttools utilities (or LaTeX3 of course).

In the \dothis/\orthat approach (copied over from xintkernel.sty) the conditions are tested and as soon as one is positive the corresponding \dothis code executes, and then all left-over tests are skipped. If all such tests fail, it is the \orthat code that executes. This (all the \ifnum tests ending in \orthat{FOO}) is expandable code, of course. Here I added to \test macro extra stuff to better display results in mwe.

If the question is only to test if given input is in given list then old-timers method of \if1\ifnum .... etc... allow to put together many \ifnum without complicated \expandafter or \fi\fi...\fi.

But here I saw other answers handling separate code outputs according to input so I did the same.

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.