0

I'm trying to make a package-free command that applies a command across a comma-separated list, and then adds the word ``and'' to the output as is grammatically appropriate. I've already gotten some help with building an \apply command, and now I'm working on an \addandtolist command. The \apply and \addandtolist commands seem to be working separately, but not in combination. Specifically, in the following test document, test 3 fails, but if you remove test 3 from the doc, tests 1 and 2 succeed:

\documentclass[letterpaper]{article}

% This command is for applying a single command to a comma-separated list of tokens, and listing the results separated by ", "
\makeatletter
\newcommand{\apply}[3][, ]{
% #1: optional separator to print between applications; default=[, ]
% #2: command to apply; 
% #3: list to apply command to 
  \def\itemsep{\def\itemsep{#1}} % first call to \itemsep prints nothing; later calls print #1
  \def\zz{\itemsep#2}%
  \@for \listelement:=#3\do{\expandafter\zz\expandafter{\listelement}}%
}
\makeatother

% The following command is for adding an "and" to a comma-separated list, if grammatically appropriate:
\newcommand{\addandtolist}[1]{%
\directlua{
    s = string.explode("#1","")
    commacounter=0
    lastcomma=-1
    for key, value in pairs(s) do 
        if value=="," then 
            commacounter=commacounter+1 
            lastcomma=key
        end
    end
    if commacounter==1 then
        s[lastcomma]=" and "
    elseif commacounter>1 then
        s[lastcomma]=", and "
    end
    t=""
    for key, value in pairs(s) do
        t=t..value
    end
    tex.sprint(t)
}
}

\begin{document}
\noindent Test 1 (succeeds): As hoped, these letters print in lowercase:\\
\apply{\lowercase}{U,V,W,X,Y}\\
\newcommand{\ComplicatedCommand}[1]{\lowercase{#1}}
\apply{\ComplicatedCommand}{U,V,W,X,Y}\\

\ 

\noindent Test 2 (succeeds): These lists get an ``and'' added to them if appropriate:\\
\addandtolist{u}\\
\addandtolist{u, v}\\
\addandtolist{u, v, w, x, y}\\

\noindent Test 3 (fails): Calling the ``addandtolist" command on the output of the apply command returns at error:\\
\addandtolist{\apply{\lowercase}{U,V,W,X,Y}}\\
\addandtolist{\apply{\ComplicatedCommand}{U,V,W,X,Y}}\\


\end{document}

Results when test 3 is excluded:

test results

Error when test 3 is included:

enter image description here

It would be cool to get the final result working with Lua, but I would be okay with non-Lua solutions as well. I do want to keep the result free of any \usepackage calls, though, because I want to avoid calling packages that could end up interfering with other packages.

Thanks a bunch for any help with this!

1
  • 1
    I would either add the and from within tex, or iterate through the list in lua, seems odd to do a combination of the two. Note the reason you give for not using a package is mostly spurious. Packages do not clash because they are packages, they clash because their definitions might be incompatible. Making essentially equivalent definitions directly in the document is just as likely to be incompatible with other code. Jan 11, 2018 at 16:19

1 Answer 1

1

Since you are iterating through the list in tex I would add and using the same type of idiom.

enter image description here

\documentclass[letterpaper]{article}

% This command is for applying a single command to a comma-separated list of tokens, and listing the results separated by ", "
\makeatletter
\newcommand{\apply}[3][, ]{%
% #1: optional separator to print between applications; default=[, ]
% #2: command to apply; 
% #3: list to apply command to 
  \def\zzitemsep{\def\zzitemsep{\ifnum\count@=\z@\space and \else#1\fi}} % first call to \zzitemsep prints nothing; later calls print #1
  \count@\z@
  \@for \listelement:=#3\do{\advance\count@\@ne}%
  \def\zz{\zzitemsep#2}%
  \@for \listelement:=#3\do{\advance\count@\m@ne\expandafter\zz\expandafter{\listelement}}%
}
\makeatother


\begin{document}
\noindent Test 1 (succeeds): As hoped, these letters print in lowercase:\\
\apply{\lowercase}{U}\\
\apply{\lowercase}{U,V}\\
\apply{\lowercase}{U,V,W,X,Y}\\
\newcommand{\ComplicatedCommand}[1]{\lowercase{#1}}
\apply{\ComplicatedCommand}{U,V,W,X,Y}\\


\end{document}

here doing two passes to count the list so you know when to add and. Note using more complete list structure eg as implemented by expl3 the list length is "known" already.

3
  • Thanks David! Your solution produces some strange side effects in the rest of a normal document. Try adding \begin{quote}Test\end{quote} on the line before your \end{document}, and you'll get a "Missing number, treated as zero" error. The same problem occurs if you add \begin{itemize} to the end of the doc. Do you know what's going on here? Jan 12, 2018 at 7:20
  • @AndrewCritch oh that's because I copied your code:-) \itemsep is a skip register used as a parameter in lists, and this redefines it to be a comma/and . Either just do it in a local group or better call \itemsep something safer such as \zzzz Jan 12, 2018 at 8:05
  • @AndrewCritch i renamed \itemsep above. Jan 12, 2018 at 8:07

You must log in to answer this question.

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