4

I'd like to define a command, with xparse's \NewDocumentCommand, which receives a comma separated list as argument, but to be able to somehow signal that some items on that list should be treated specially.

My first thought for this was to use bracing. And I came up with something like:

\documentclass{article}

\ExplSyntaxOn \NewDocumentCommand \mylist { > { \SplitList { , } } m } { \tl_map_inline:nn {#1} { \tl_if_head_is_group:nTF {##1} { Hi,~I'm~special~##1! \par } { ##1 \par } } } \ExplSyntaxOff

\begin{document}

\mylist{item1,item2,{{item3}},item4,{{item5}},item6}

\end{document}

Which prints, as expected:

enter image description here

And, in case I cannot rely on the extra braces being just expanded away in typesetting, as with the example above, I could go with something like:

\cs_new:Npn \__my_tl_set_unbraced:Nn #1#2
  {
    \tl_if_head_is_group:nTF {#2}
      { \tl_set:Nn #1 #2 }
      { \tl_set:Nn #1 {#2} }
  }

after I've retrieved the "signal", and can then pass a variable set with this function as an argument for this to work as any other "non special" item on the list.

While this all works, and feels OK at first sight, I find it a little too "creative", to the point of getting weary, and got wondering if there are alternatives. Hence this question.

What I'd like to know is: is this a reasonable procedure? (from both the TeXnical and the user interface sides). Do you see any blatant caveats in it? Are there any good alternatives to it?

gusbrs
  • 13,740
  • 1
    The problem with \tl_if_head_is_group:nTF for this case is that both {item3} and {i}tem3 will be true. Other than that, your code looks correct, but you have to be sure what syntax you want to impose on your users (assuming it's not just for personal use). I'd find using {{item}} a bit cumbersome. – Phelype Oleinik Aug 20 '21 at 02:23
  • Hi @PhelypeOleinik, thanks for the comment. I hadn't thought of {i}tem3, indeed. I don't expect it to be a problem in the case, since I don't expect to get this kind of input. But well pointed. And, agreed, {{item}} is cumbersome. In a way it is the fact that I don't really like it that triggered me to ask... Do you know any good alternatives? – gusbrs Aug 20 '21 at 02:28
  • It depends, I think, on what's the purpose of the list. You could use *item (since * is already ubiquitously used to mean “treat this differently”), but then you'd need extra gymnastics to actually add an item with a * in its name because it would be confused with the marker: you'll have this problem with any marker you choose. That's why it depends quite a bit on what the list contains. A safer bet is to write item1,\special{item2} for a good choice on the name of the macro \special (but then that's a lot to type, so not really good either). – Phelype Oleinik Aug 20 '21 at 02:35
  • The list items are reference labels. I don't expect much trouble in the content, but I would not like to restrain user choice of characters. \special may be safer, but I think it is more cumbersome than the double bracing, to the user, and to handle in the implementation side (as far as I can tell). – gusbrs Aug 20 '21 at 02:40
  • Well, once you choose the marker, you can write it like \str_if_eq:eeTF { \tl_head:n {##1} } { * } { Hi,~I'm~special~\use_none:n ##1! \par } { ##1 \par } – Phelype Oleinik Aug 20 '21 at 02:46
  • @PhelypeOleinik Interesting. It might be it. cleveref uses an "empty item" following the signaled one for the purpose, it is also good, but not ideal. Regarding *, you said it is "ubiquitously used to mean “treat this differently”". You mean here starred commands, or in this context? I personally don't recall seeing it used for this particular purpose. – gusbrs Aug 20 '21 at 02:57
  • (It is a little late here, I'll come back to this tomorrow morning.) – gusbrs Aug 20 '21 at 03:01
  • Yeah, I meant starred commands (not for this particular purpose, but generally). Some commands behave slightly differently when used with a * (see \newcommand), and some behave in a completely different way (for example the tabular and tabular* environments), so a * seems fitting. – Phelype Oleinik Aug 20 '21 at 03:03
  • For the special items I'd use something like |special item| (the | is not used in normal typesetting). – egreg Aug 20 '21 at 08:16
  • @PhelypeOleinik Thanks again for your thoughts. I'm not sure the starred command idiom translates well for this case. But, indeed, it is a matter of choosing "something". egreg came up with a good one, I think. (And sorry for leaving mid discussion yesterday, I was in real need to get some rest). – gusbrs Aug 20 '21 at 10:06
  • @gusbrs No problem at all. It was pretty late around here as well ;-) Surrounding the items with |...| looks good indeed! – Phelype Oleinik Aug 20 '21 at 11:36
  • In case of leading { you can check if gobbling the first undelimited argument yields emptiness. If so, the entire argument is nested in braces. – Ulrich Diez Aug 20 '21 at 12:50
  • @UlrichDiez That is a good sanity check to do if using bracing, indeed. Thanks for the suggestion! – gusbrs Aug 20 '21 at 12:53

2 Answers2

6

Here is a latex2e way, using listofitems. Special items contain a *, which should appear at the end of the item. The special character denoter can be changed by editing the last entry in the \setsepchar list.

\documentclass{article}
\usepackage{listofitems}
\newcommand\processmylist[1]{%
  \setsepchar{,/*}%
  \readlist*\mylist{#1}%
  \foreachitem\z\in\mylist[]{%
    \ifnum\listlen\mylist[\zcnt]=1
      \z
    \else
      SPECIAL: \mylist[\zcnt,1]%
    \fi
    \\
  }%
}
\begin{document}
\noindent\processmylist{item1, item2, item3*, item4, item 5*}
\end{document} 

enter image description here

  • Hi, Steven, thank you! I didn't know listofitems, I will check it out. But, for the use case, I think this is too small a task to add a dependency. I would like to handle this with the kernel, if possible. – gusbrs Aug 20 '21 at 02:34
  • @gusbrs Understood. By the way, listofitems runs in plain TeX, as well with \input listofitems. – Steven B. Segletes Aug 20 '21 at 02:49
  • I was just taking a look at the documentation, and it seems quite interesting. Thanks for pointing me to it. – gusbrs Aug 20 '21 at 02:51
  • 1
    @gusbrs You are welcome. If you search this site for listofitems, you will also find a number of ways in which it can be applied. The nested parsing is quite powerful. – Steven B. Segletes Aug 20 '21 at 03:23
4

Your code is reasonable and works, but requires {{special item}}, because of TeX's rule that one pair of braces around arguments is stripped off when unbalanced token lists wouldn't arise.

I'd use |special item| that's simpler to type and avoid \SplitList: you need to go to the expl3 level anyway.

\documentclass{article}

\ExplSyntaxOn \NewDocumentCommand \mylist { m } { \gusbrs_mylist:n { #1 } }

\cs_new_protected:Nn \gusbrs_mylist:n { \clist_map_inline:nn {#1} { \str_if_eq:eeTF { \tl_head:n { ##1 } } { | } { __gusbrs_mylist_special:w ##1 } { ##1 \par } } }

\cs_new:Npn __gusbrs_mylist_special:w | #1 | { Hi,~I'm~special~#1! \par } \ExplSyntaxOff

\begin{document}

\mylist{item1,item2,|item3|,item4,|item5|,item6}

\end{document}

As an exercise, you can add a test for the trailing | before calling the :w function and raise a warning or error.

enter image description here

After reading comments, you can also signal the special items with just a prefix character, here |.

\documentclass{article}

\ExplSyntaxOn \NewDocumentCommand \mylist { m } { \gusbrs_mylist:n { #1 } }

\cs_new_protected:Nn \gusbrs_mylist:n { \clist_map_inline:nn {#1} { \str_if_eq:eeTF { \tl_head:n { ##1 } } { | } { __gusbrs_mylist_special:e { \tl_tail:n { ##1 } } } { ##1 \par } } }

\cs_new:Nn __gusbrs_mylist_special:n { Hi,~I'm~special~#1! \par } \cs_generate_variant:Nn __gusbrs_mylist_special:n { e }

\ExplSyntaxOff

\begin{document}

\mylist{item1,item2,|item3,item4,|item5,item6}

\end{document}

egreg
  • 1,121,712
  • Hi, egreg, thank you! | is a good idea for a character here. I might even evade the exercise and demand only the leading one, it would not be a bad thing for the meaning I'm trying to convey. :-) – gusbrs Aug 20 '21 at 10:00
  • If I recall correctly, you have the canonical here on the site on "characters allowed in labels" (https://tex.stackexchange.com/a/18312/105447). Theoretically, | is allowed for labels, isn't it? Have you ever seen people using it for this? – gusbrs Aug 20 '21 at 10:03
  • @gusbrs No, never. You might want to use * as a single prefix for special items. – egreg Aug 20 '21 at 10:19
  • That was Phelype's suggestion too. I'll think about the alternatives. This is a functionality I'd like to exist, but I believe should be used rarely. So, I'm a little divided whether to provide such an ad hoc syntax, or simply to receive the "special item" in the optional argument. That would involve repetition, but keep the syntax "clean". Anyway, I have to think this through. The technical problem is well sorted here. Thanks again! – gusbrs Aug 20 '21 at 10:26
  • @gusbrs I added the code for | as a prefix. – egreg Aug 20 '21 at 10:36
  • egreg, thanks! And, may I ask about a side technical detail? I see you used the e signature abundantly in your answer, how concerned should I be with the "much slower on pre-2019 non LuaTeX engines" caveat? – gusbrs Aug 20 '21 at 10:54
  • Btw, the edit is much appreciated, but when I said I was considering "demanding only the leading one", I had meant the users, not you. :-) – gusbrs Aug 20 '21 at 10:59
  • @gusbrs Users with a pre-2019 TeX Live should be just a few and probably not really concerned with speed. – egreg Aug 20 '21 at 11:29
  • Good point. I will give the e signature with more attention. Sometimes it is extremely handy, and I kept pondering that warning. But I think you are right. Thanks yet again! – gusbrs Aug 20 '21 at 11:33