2

Aim

I want a command to write "where #1 is an arbitrary constant", "where #1 are arbitrary constants", "where #1 is an arbitrary function of #2", or "where #1 are arbitrary functions of #2", as appropriate.

Background

Usually it's enough to let singular-plural detection rely on the presence or absence of a comma in #1. For that, the following function works fine.

\documentclass{article}
\usepackage{xstring,xargs,xifthen,amsmath}
\newcommandx{\arbitrary}[3][1=\text{ where }, 3=]{%
    #1#2%
    \IfSubStr{#2}{,}{\text{ are}}{\text{ is an}}%
    \text{ arbitrary}%
    \ifthenelse{\equal{#3}{}}{\text{ constant}}{\text{ function}}%
    \IfSubStr{#2}{,}{\text{s}}{}%
    \ifthenelse{\equal{#3}{}}{}{\text{ of }#3}
}
\begin{document}
    \begin{align*}
        & \arbitrary{a} \\ % where a is an arbitrary constant
        & \arbitrary{a,b} \\ % where a,b are arbitrary constants
        & \arbitrary{f}[x] \\ % where f is an arbitrary function of x
        & \arbitrary{f,g}[x] % where f,g are arbitrary functions of x
    \end{align*}
\end{document}

But sometimes I want to pluralise a singular-looking argument, like

2a_n+1, \text{ where $a_n$ are arbitrary constants, } n \in \{1,2,\dots\}

For this I'm trying to implement a backup detection mechanism, using an optional argument. This involves defining two conditions on which the function will treat #1 as plural: if #1 contains a comma, or if #4=p for plural.

\documentclass{article}
\usepackage{xstring,xargs,xifthen}
\newcommandx{\arbitrary}[4][1=\text{ where }, 3=, 4=]{%
    #1#2%
    \ifthenelse{%
        1=\IfSubStr{#2}{,}{1}{0}\OR%
        \equal{#4}{p}%
    }{\text{ are}}{\text{ is}}%
    \text{ arbitrary}%
    \ifthenelse{\equal{#3}{}}{\text{ constant}}{\text{ function}}%
    \ifthenelse{%
        1=\IfSubStr{#2}{,}{1}{0}\OR%
        \equal{#4}{p}%
    }{\text{s}}{}%
    \ifthenelse{\equal{#3}{}}{}{\text{ of }#3}
}
\begin{document}
    I wish this function worked.
\end{document}

Problem

LaTeX seems not to like the syntax \ifthenelse{1=\IfSubStr{ab}{a}{1}{0}}{1}{0}.

\documentclass{article}
\usepackage{xstring,xifthen}
\begin{document}
    \IfSubStr{ab}{a}{1}{0} % returns 1
    \ifthenelse{1=1}{1}{0} % returns 1
    \ifthenelse{1=\IfSubStr{ab}{a}{1}{0}}{1}{0} % returns error
\end{document}
2

An extended version that also copes with multivariate functions. The *-version forces plural.

\documentclass{article}
\usepackage{amsmath}

\ExplSyntaxOn

\NewDocumentCommand{\arbitrary}{smo}
 {% #1 = boolean for forcing plural
  % #2 = mandatory argument
  % #3 = optional argument for the variable
  \IfBooleanTF{#1}
   { \bool_set_true:N \l_mjc_arbitrary_plural_bool }
   { \bool_set_false:N \l_mjc_arbitrary_plural_bool }
  \IfNoValueTF{#3}
   { \mjc_arbitrary_constant:n { #2 } }
   { \mjc_arbitrary_function:nn { #2 } { #3 } }
 }

\bool_new:N \l_mjc_arbitrary_plural_bool
\seq_new:N \l__mjc_arbitrary_variables_seq
\seq_new:N \l__mjc_arbitrary_variables_out_seq

\cs_new_protected:Nn \mjc_arbitrary_constant:n
 {
  \bool_lazy_or:nnTF
   { \l_mjc_arbitrary_plural_bool }
   { \int_compare_p:n { \clist_count:n { #1 } > 1 } }
   { \mjc_arbitrary_phrases:nnnn { #1 } { are } { constants } { } }
   { \mjc_arbitrary_phrases:nnnn { #1 } { is~an } { constant } { } }
 }

\cs_new_protected:Nn \mjc_arbitrary_function:nn
 {
  \bool_lazy_or:nnTF
   { \l_mjc_arbitrary_plural_bool }
   { \int_compare_p:n { \clist_count:n { #1 } > 1 } }
   { \mjc_arbitrary_phrases:nnnn { #1 } { are } { functions } { #2 } }
   { \mjc_arbitrary_phrases:nnnn { #1 } { is~an } { functions } { #2 } }
 }

\cs_new_protected:Nn \mjc_arbitrary_phrases:nnnn
 {% #1 = object, #2 = verb, #3 = type, #4 = optional variable
  \mode_if_math:TF 
   { \text { \__mjc_arbitrary_phrases:nnnn { #1 } { #2 } { #3 } { #4 } } }
   { \__mjc_arbitrary_phrases:nnnn { #1 } { #2 } { #3 } { #4 } }
 }
\cs_new_protected:Nn \__mjc_arbitrary_phrases:nnnn
 {
  where~$#1$~#2~arbitrary~#3
  \tl_if_blank:nF { #4 }
   {
    \seq_set_from_clist:Nn \l__mjc_arbitrary_variables_seq { #4 }
    \c_space_tl of~the~variable
    \int_compare:nT { \seq_count:N \l__mjc_arbitrary_variables_seq > 1 } { s }
    \nobreakspace
    \__mjc_arbitrary_variables:
   }
 }
\cs_new_protected:Nn \__mjc_arbitrary_variables:
 {
  \seq_set_map:NNn \l__mjc_arbitrary_variables_out_seq \l__mjc_arbitrary_variables_seq { $##1$ }
  \seq_use:Nnnn \l__mjc_arbitrary_variables_out_seq { ~and~ } { ,~ } { ,~and\nobreakspace }
 }
\ExplSyntaxOff

\begin{document}

\begin{align*}
& \arbitrary{a} \\ % where a is an arbitrary constant
& \arbitrary*{a_n} \\ % where a_n are arbitrary constants
& \arbitrary{a,b} \\ % where a,b are arbitrary constants
& \arbitrary{f}[x] \\ % where f is an arbitrary function of x
& \arbitrary{f,g}[x] \\% where f,g are arbitrary functions of x
& \arbitrary{f}[x,y] \\ % where f is an arbitrary function of x and y
& \arbitrary{f,g}[x,y] \\% where f,g are arbitrary functions of x, y, and z
& \arbitrary*{f_n}[x] \\ % where f is an arbitrary function of x
& \arbitrary*{f_n}[x,y] % where f is an arbitrary function of x and y
\end{align*}

\arbitrary{a},
\arbitrary*{a_n},
\arbitrary{a,b},
\arbitrary{f}[x],
\arbitrary{f,g}[x],
\arbitrary*{f_n}[x].

\end{document}

I only use \text if the command is found in math mode; in text mode you don't want to make the thing unbreakable across lines.

Slight complication is using ~ where you want a space, but it just requires a bit of practice.

enter image description here

2

I use etoolbox to condition on the presence of , within your first argument (via \patchcmd{<cmd>}{<search>}{<replace>}{<success>}{<failure>}). That provides the first conditional branch. Then I condition on the existence of a closing optional argument using xparse's \IfValueTF{<arg>}{<true>}{<false>}.

enter image description here

\documentclass{article}

% \usepackage{xparse}% Only if you have LaTeX < 2020-10
\usepackage{etoolbox,amsmath}

\NewDocumentCommand{\arbitrary}{m o}{
  \def\firstarg{#1}% Store mandatory argument
  \text{ where }
  \firstarg
  \IfValueTF{#2}
    {\patchcmd{\firstarg}{,}{,}
      {\text{ are arbitrary functions of } #2}% Contains a comma , => plural
      {\text{ is an arbitrary function of } #2}}% Does not contain a comma , => singular
    {\patchcmd{\firstarg}{,}{,}
      {\text{ are arbitrary constants}}% Contains a comma , => plural
      {\text{ is an arbitrary constant}}}% Does not contain a comma , => singular
}
\begin{document}

\begin{align*}
  & \arbitrary{a} \\ % where a is an arbitrary constant
  & \arbitrary{a,b} \\ % where a,b are arbitrary constants
  & \arbitrary{f}[x] \\ % where f is an arbitrary function of x
  & \arbitrary{f,g}[x] % where f,g are arbitrary functions of x
\end{align*}

\end{document}

If you want to force a plural output for a singular item, you could adapt \arbitrary to take a star - \arbitrary*. This is how you'd condition using that:

\documentclass{article}

% \usepackage{xparse}% Only if you have LaTeX < 2020-10
\usepackage{etoolbox,amsmath}

\NewDocumentCommand{\arbitrary}{s m o}{
  \def\firstarg{#2}% Store mandatory argument
  \text{ where }
  \firstarg
  \IfBooleanTF{#1}
    {\text{ are arbitrary constants}}% Starred version => plural
    {
    \IfValueTF{#3}
      {\patchcmd{\firstarg}{,}{,}
        {\text{ are arbitrary functions of } #3}% Contains a comma , => plural
        {\text{ is an arbitrary function of } #3}}% Does not contain a comma , => singular
      {\patchcmd{\firstarg}{,}{,}
        {\text{ are arbitrary constants}}% Contains a comma , => plural
        {\text{ is an arbitrary constant}}}% Does not contain a comma , => singular
    }
}
\begin{document}

\begin{align*}
  & \arbitrary{a} \\ % where a is an arbitrary constant
  & \arbitrary*{a_n} \\ % where a_n are arbitrary constants
  & \arbitrary{a,b} \\ % where a,b are arbitrary constants
  & \arbitrary{f}[x] \\ % where f is an arbitrary function of x
  & \arbitrary{f,g}[x] % where f,g are arbitrary functions of x
\end{align*}

\end{document}
9
  • This seems to replicate the functionality of the original command, but does it handle the exception I described? If my original syntax had worked, I would have written \arbitrary{a_n}[][p] and got "where $a_n$ are arbitrary constants". Could you clarify how I can do that with your command?
    – mjc
    Apr 8 at 4:08
  • @mjc: Sure it does. You should try it! The only thing that distinguishes between singular or plural usage is a , (comma) in the first/mandatory argument. Since a_n doesn't have a ,, it's considered singular. Note though that it won't work if you pass a macro that has your list of items in there (for example, \newcommand{\itemlist}{a_n, b_n} and then use \arbitrary{\itemlist} will print a singular phrase).
    – Werner
    Apr 8 at 4:59
  • "Since a_n doesn't have a , it's considered singular.". Right; that's the problem that motivated the OP. I want the option to treat singular-looking expressions like a_n as plural.
    – mjc
    Apr 8 at 5:02
  • @mjc - You wrote, "I want the option to treat singular-looking expressions like a_n as plural." Do your readers somehow know that a_n is a plural object even though it sure looks like a singular object?
    – Mico
    Apr 8 at 5:42
  • 1
    @mjc: How about this as an option? For the odd case you want to force as plural, use \arbitrary*{a_n}.
    – Werner
    Apr 8 at 14:47

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.