0

I'm working on a command that involves checking several arguments to see if they're colours, and doing something with those that are. I can't get \tl_if_eq to recognise matches.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\IsColour}{m}{
    \clist_if_in:nnTF { 
        red, 
        orange, 
        yellow, 
        green, 
        blue, 
        purple, 
        white, 
        gray, 
        brown, 
        black 
    } { #1 } { TRUE } { FALSE }
}

\NewDocumentCommand{\TestA}{}{
    \tl_set:Nn \l_test { TRUE }
    \tl_if_eq:NnTF \l_test { TRUE } { TRUE } { FALSE }
}

\NewDocumentCommand{\TestB}{}{
    \tl_set:Nn \l_test { \IsColour{red} }
    \tl_if_eq:NnTF \l_test { TRUE } { TRUE } { FALSE }
}

\ExplSyntaxOff

\begin{document}
    \TestA % works as desired
    \hspace{1em}
    \IsColour{red} % works as desired
    \hspace{1em}
    \TestB % doesn't work
\end{document}

Edit 1 in response to egreg's request for more detail.

The ultimate aim is to create a \Style function with two mandatory arguments, the first being a comma- or semicolon-separated list of parameters, and the second being the body of text to which the style should be applied.

I'm interested in the possibility of an unordered syntax for the list of parameters, without using explicit key-value pairs. For that, I'm trying to check each argument to see what type of parameter it is (e.g. colour, emphasis, etc.) and use it accordingly, assuming that no parameter belongs to multiple categories.

The finished product would look like this:

\Style{red,italic}{hi} % returns \textit{\color{red}hi}
\Style{italic,red}{hi} % returns \textit{\color{red}hi}

My specific question could also be phrased as: how can I check if the output of a function matches a specified value?

Edit 2 in response to David Carlisle's suggestion.

I guess I'm missing something because I can't get this to work.

\documentclass{article}

\def\ZZred#1{\textcolor{red}{#1}}

\newcommand{\Style}[2]{
    \csname ZZ#1\endcsname{#2}
}

\begin{document}
    \Style{red}{hi}
\end{document}
10
  • your \IsColour is not expandable. May 6, 2021 at 21:49
  • @UlrikeFischer I don't know that much about expansion. \NewExpandableDocumentCommand doesn't seem to fix it. Can you give any more detail?
    – mjc
    May 6, 2021 at 21:53
  • You're comparing \IsColour{red} with TRUE and of course this will return false. Please, add some more details about your aim.
    – egreg
    May 6, 2021 at 21:59
  • 1
    This comment will presumably be removed by some LaTeX3 team member, but just to let you know: no, it is not your fault if you get confused by the LaTeX3 manual. Unlike the TeX book and the pgf manual, say, it is not really written to enable others to do things. If it was, it would have more explicit examples which really illustrate things, and in particular acknowledge the shortcomings and limitations.
    – user241266
    May 7, 2021 at 2:01
  • 2
    @user241266 interface3 is meant as a reference manual, hence the relatively small number of examples. As far as possible, it does detail formally the nature of each function including any restrictions. We know that a 'Programming using expl3' guide is needed, but these things are hard to write. We have pointed to a very good web resource which aims to do that: latex-project.org/news/2020/11/09/l3-tutorial.
    – Joseph Wright
    May 7, 2021 at 7:39

3 Answers 3

2

My specific question could also be phrased as: how can I check if the output of a function matches a specified value?

Not every macro returns an "output" that can be tested as a value. Some execute rather complex instructions like assignments or writing to a file or selecting a font or defining something. In a test like yours you can only use "expandable" macros without such instructions. In interface3.pdf expandable macros are marked with an asterix, or you can test by trying to expand yourself:

\tl_set:Nx\l_tmpa_tl{\IsColour{red}}
\tl_show:N\l_tmpa_tl

If the outpout is wrong or if you get errors it is not expandable.

It is sometimes possible to rewrite a macro and make it expandable, but quite often this is not needed. In your case you can for example change your \IsColour command to set a boolean (but like David I think it is better to avoid too many tests and to define commands):

\documentclass{article}
\usepackage{xparse,color}

\ExplSyntaxOn
\bool_new:N \l__mjc_iscolour_bool
\tl_new:N \l__mjc_currentcolour_tl 

\NewDocumentCommand{\IsColour}{m}{
    \clist_if_in:nnTF {
        red,
        orange,
        yellow,
        green,
        blue,
        purple,
        white,
        gray,
        brown,
        black
    } { #1 } 
    { 
      \bool_set_true:N \l__mjc_iscolour_bool
      \tl_set:Nn \l__mjc_currentcolour_tl {#1}
    } 
    { 
      \bool_set_false:N \l__mjc_iscolour_bool
      \tl_set:Nn \l__mjc_currentcolour_tl {}      
    }
}


\NewDocumentCommand{\TestA}{}{
    \tl_set:Nn \l_test { TRUE }
    \tl_if_eq:NnTF \l_test { TRUE } { TRUE } { FALSE }
}

\NewDocumentCommand{\TestB}{m}{
   \IsColour{#1}
   \bool_if:NTF \l__mjc_iscolour_bool
    { \textcolor{\l__mjc_currentcolour_tl}{TRUE} }
    { FALSE}
}

\ExplSyntaxOff

\begin{document}
    \TestB{red} 
    \TestB{violett}
\end{document}

enter image description here

1
  • One could create an expandable \IsColour with \str_case:nnTF.
    – Skillmon
    May 10, 2021 at 6:01
2

Rather than do multiple test you can just execute the list as a sequence of nested commands.

enter image description here

\documentclass{article}
\usepackage{color}

\def\Style#1#2{\xStyle{#2}#1,STOP,}
\def\xStyle#1#2,{\csname Z#2\endcsname{#1}}

\def\Zred#1{\xStyle{\textcolor{red}{#1}}}
\def\Zgreen#1{\xStyle{\textcolor{green}{#1}}}
\def\Zitalic#1{\xStyle{\textit{#1}}}
\def\Zbold#1{\xStyle{\textbf{#1}}}
\def\ZSTOP#1{#1}


\begin{document}


\Style{red,italic}{hi}

\Style{italic,red}{hi}

\Style{italic,green,bold}{hi}


\end{document}
6
  • Thanks for this. This looks good for a restricted case of what I have in mind, but my "etc." (which perhaps I should have emphasised more) refers to things which I think this approach may not cover, e.g. a width for a \parbox.
    – mjc
    May 6, 2021 at 23:45
  • @mjc it could set a width of a parbox May 6, 2021 at 23:53
  • Really? \csname Z#2\endcsname{#1} would have to take a measurement, e.g. 2cm, as #2, which would mean doing \def\Z2cm, no?
    – mjc
    May 6, 2021 at 23:57
  • @mjc you'd have to extend xstyle a bit to if #2 is a length \parbox else \csname, exactly what you do depends if all lengths need to be parbox or you need different length things eg a {parbox5cm,framebox3cm} or whatever testing for a length is annoyingly tricky but you'd have to do that whatever looping you used. Obviously it's easier if the lengths are prefixed as in parbox5cm as then you just look for the prefix and the following part is a lengt or an error May 7, 2021 at 0:08
  • I have an expl3 regex command for identifying measurements, courtesy of this answer: tex.stackexchange.com/a/595225/235895 (part of the reason I felt close to a full solution with my initial approach). If you mean parbox5cm would be the parameter passed to the command, I'd like to avoid that if possible, as that's no shorter than the key-value width=5cm syntax I'm trying to avoid.
    – mjc
    May 7, 2021 at 0:18
0

You may choose to use functional package if you want to make compostition of functions work like other modern programming languages such as Lua: with this package, the evaluation of composite functions is from inside to outside.

\documentclass{article}

\usepackage{functional}

\IgnoreSpacesOn

\PrgNewFunction \IsColour {m} {
  \ClistIfInTF { 
        red, 
        orange, 
        yellow, 
        green, 
        blue, 
        purple, 
        white, 
        gray, 
        brown, 
        black 
   } { #1 } { \Result{TRUE} } { \Result{FALSE} }
}

\PrgNewFunction \TestA {} {
  \TlSet \lTestTl { TRUE }
  \TlIfEqTF { \Value\lTestTl } { TRUE } { \Result{TRUE} } { \Result{FALSE} }
}

\PrgNewFunction  \TestB {} {
  \TlSet \lTestTl { \IsColour{red} }
  \TlIfEqTF { \Value\lTestTl } { TRUE } { \Result{TRUE} } { \Result{FALSE} }
}

\IgnoreSpacesOff

\begin{document}

\TestA % works as desired
\hspace{1em}
\IsColour{red} % works as desired
\hspace{1em}
\TestB % works as desired

\end{document}

enter image description here

You must log in to answer this question.

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