4

What is a good way to write a definition of the type \fun below

\fun{abc1 EDF1 xyz1 efg jkl abc2 EDF2 xyz2 efg jkl abc3 EDF3 xyz3 efg jkl abc4 EDF2 xyz4 ...}

where the number of arguments is not known in advance, may be 1, or may be more than 9, like 12, and the result is as below

abc1 EDF1 \form{xyz1} efg jkl abc2 EDF2 \form{xyz2} efg jkl abc3 EDF3 \form{xyz3} efg jkl abc4 EDF2 \form{xyz4} ...

Assume for answers \form is same type as \newcommand{\form}[1]{\emph{#1}}. But delay expansion until the end. Just in case.

Probably requires several nested \def cases taking two variables each and a \if to decide when to halt the loop ... like when allowing more than 9 arguments to a macro? Or use etoolbox for something better?

If separator words efg jkl are not the same from one place to the next, certain they they are of some specific length, assume efg jkl <=> always 9 characters separating one input from the next. While abc EDF xyz are of different lengths possibly, but always three separate words.

CLARIFICATION: The xyz can be arbitrary. But the sentence is periodic in that the 3rd, 8th, 13th, 18th word is \form wrapped. If it exists. At least 3 words exists. But can be 3, and if not 3 then 8, and if not 8 then 13, as long as needed, ends when no separator behind three words which can all differ ... The separator " efg jkl " does repeat with certainty however and can be caught to parse.

  • \fun{abc1 EDF1 xyz1 efg jkl abc2 EDF2 xyz2 efg jkl abc3 EDF3 xyz3 efg jkl abc4 EDF2 xyz4} and \fun{abc1 EDF1 xyz1} and \fun{abc1 EDF1 xyz1 efg jkl abc2 EDF2 xyz2 efg jkl abc3 EDF3 xyz3 efg jkl abc4 EDF2 xyz4 ... efg jkl abc12 EDF12 xyz12}, etc, equally possible. Some kind of loop required. But note that there would not appear any numbers in the input phrase; the numbers are here to illustrate how long happens to be the list. Just the list is always entered as a specific kind of sentence. – Guido Jorg Mar 22 at 22:13
  • Splitting such an input at spaces is easy, and testing for some regular expression too (with expl3) but your description is a bit confusing. – Ulrike Fischer Mar 22 at 22:19
  • Without knowing the rules for applying \form it's really difficult to answer. – egreg Mar 22 at 22:22
  • @GuidoJorg I'm not asking what \form should do, but to which parts it should be applied. Some real world examples might help to understand. As the question stands, it's impossible to guess. – egreg Mar 22 at 22:41
  • @egreg to the 3rd, 8th, 13th, 18th ... word in the sentence. Separator can be assumed to repeat. \form affects only these parts. And sentence can be arbitrarily long and will end without the separator. – Guido Jorg Mar 22 at 22:46
5
\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\fun}{m}
 {
  % split the input at the spaces
  \seq_set_split:Nnn \l_tmpa_seq { ~ } { #1 }
  % use a counter for knowing where we are
  \int_zero:N \l_tmpa_int
  % map the sequence
  \seq_map_inline:Nn \l_tmpa_seq
   {% one more step
    \int_incr:N \l_tmpa_int
    \int_compare:nTF { \int_mod:nn { \l_tmpa_int - 3 } { 5 } = 0 }
     {% if we're at the 3rd, 5th, 8th, 13th, ... item, apply \form
      \form { ##1 }
     }
     {% otherwise just deliver the item
      ##1
     }
    % if not at the last, add a space
    \int_compare:nT { \l_tmpa_int < \seq_count:N \l_tmpa_seq } { ~ }
   }
 }
\ExplSyntaxOff

\NewDocumentCommand{\form}{m}{\emph{#1}}

\begin{document}

\raggedright

\fun{Non eram nescius Brute cum quae summis ingeniis exquisitaque 
doctrina philosophi Graeco sermone tractavissent ea Latinis 
litteris mandaremus fore ut hic noster labor in varias 
reprehensiones incurreret Nam quibusdam et iis quidem non 
admodum indoctis totum hoc displicet philosophari Quidam 
autem non tam id reprehendunt si remissius agatur sed tantum 
studium tamque multam operam ponendam in eo non arbitrantur 
Erunt etiam et ii quidem eruditi Graecis litteris contemnentes 
Latinas qui se dicant in Graecis legendis operam malle consumere 
Postremo aliquos futuros suspicor qui me ad alias litteras 
vocent genus hoc scribendi etsi sit elegans personae tamen 
et dignitatis esse negent}

\end{document}

enter image description here

  • @GuidoJorg That's very vague. – egreg Mar 23 at 11:10
  • @GuidoJorg That makes things even more obscure. – egreg Mar 23 at 13:52
  • Simplest example: \fun{{Non eram nescius Brute cum quae summis ingeniis exquisitaque doctrina philosophi Graeco}}' displays Non eram nescius Brute cum quae summis ingeniis exquisitaque doctrina philosophi Graeco` not the result of \fun{Non eram nescius Brute cum quae summis ingeniis exquisitaque doctrina philosophi Graeco}. Due to extra {}. What should be done so \fun{Non eram nescius Brute cum quae summis ingeniis exquisitaque doctrina philosophi Graeco} displays when \fun{{Non eram nescius Brute cum quae summis ingeniis exquisitaque doctrina philosophi Graeco}} is entered? – Guido Jorg Mar 23 at 14:05
  • @GuidoJorg Why should \fun{{...}} give the same result as \fun{...}? I see no reason to. – egreg Mar 23 at 14:06
  • By itself: A displays as {A}. In the use case, composition is desirable. If the text into \fun is generated by some macro, not directly entered, \fun won't process it. Because it seems the result is treated as {A} rather than A. Even though it is A. Single object. \fun sees no separators when Ab Cd Ef = \macro{test} and \fun{\macro{test}}. – Guido Jorg Mar 23 at 14:13
5

(updated this answer signficantly to allow for multiple (expandable) macros in the argument of \fun.)

Here's a LuaLaTeX-based solution. It can handle multiple, expandable macros in the argument of \fun. The Lua code first splits the (expanded) input string into separate words, taking note of punctuation characters, if present. It then proceeds to print them, encasing the 3rd, 8th, 13th, 18th words in the \form macro. (Mathematically speaking, the selection criterion is that the word's position in the table, modulo 4, equals 3.) Non-ASCII UTF8-encoded characters are fine (because the unicode.utf8.gmatch function rather than the "basic" string.gmatch function is employed.)

enter image description here

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{luacode} % for 'luacode' environment and '\luastring' macro

%% Lua-side code
\begin{luacode}
function do_fun ( s )
  words = {}  -- initialize a Lua table
  -- split 's' into constituent words
  for word in unicode.utf8.gmatch ( s , "%w+%p?" ) do 
     table.insert ( words , word ) 
  end
  -- apply "form" macro at 3rd, 8th, 13th, etc words
  for i = 1 , #words do
    if i%5 == 3 then
       tex.sprint ( "\\form{"..words[i].."} " )
    else
       tex.sprint ( words[i].." " )
    end
  end
end
\end{luacode}

%% TeX-side code
\newcommand\fun[1]{\directlua{do_fun(\luastring{#1})}}
\newcommand\form[1]{\emph{#1}}
\newcommand\blurbA{Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut purus elit, vestibulum ut, placerat ac, adipiscing vitae, felis. Curabitur dictum gravida mauris.}
\newcommand\blurbB{Nam arcu libero, nonummy eget, consectetuer id, vulputate a, magna. Donec vehicula augue eu neque. Pellentesque habitant.}
\newcommand\blurbC{abc1 EDF1 xyz1 efg jkl abc2 EDF2 xyz2 efg jkl abc3 EDF3 xyz3 efg jkl abc4 EDF4 xyz4 efg jkl abc5 EDF5 xyz5 efg jkl abc6 EDF6 xyz6 efg jkl abc7 EDF7 xyz7 efg jkl abc8 EDF8 xyz8 efg jkl abc9 EDF9 xyz9 efg jkl abc10 EDF10 xyz10 efg jkl abc11 EDF11 xyz11 efg jkl abc12 EDF12 xyz12 efg jkl abc13 EDF13 xyz13}

\begin{document}
\raggedright
\fun{\blurbA\blurbB\blurbC} 
\end{document}
  • Probably cannot use for a journal but LuaLaTex solutions are very elegant. I will look at LuaTex some more for personal use, have not used it much before. – Guido Jorg Mar 22 at 23:02
  • 1
    @GuidoJorg - Some, but certainly not all, journals by now allow LuaLaTeX in addition to pdfLaTeX. – Mico Mar 22 at 23:24
3
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{listofitems,tabto}
\newcounter{formtrigger}
\newcommand\form[1]{\emph{#1}}
\newcommand\fun[1]{%
  \setsepchar{ }%
  \readlist*\funlist{#1}%
  \setcounter{formtrigger}{2}%
  \foreachitem\x\in\funlist[]{%
    \stepcounter{formtrigger}%
    \ifnum\theformtrigger=5\relax
      \form{\x}\setcounter{formtrigger}{0}%
    \else%
      \x%
    \fi%
    \ %
  }
}
\begin{document}
\fun{abc1 EDF1 xyz1 efg jkl abc2 EDF2 xyz2 efg jkl abc3 EDF3 xyz3 efg jkl abc4 EDF2 xyz4 ...}
\end{document}

enter image description here

ORIGINAL ANSWER

The listofitems package can grab these inputs very easily, preserving the original tokens without expansion.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{listofitems,tabto}
\newcommand\form[1]{\emph{#1}}
\newcommand\fun[1]{%
  \setsepchar{ }%
  \readlist\funlist{#1}%
  \foreachitem\x\in\funlist[]{%
    Argument \xcnt{} is\tabto{1.3in}``\detokenize\expandafter{\x}'': 
    \tabto{2.5in}\x\par
  }%
}
\begin{document}
\fun{abc1 EDF1 xyz1 efg jkl abc2 EDF2 xyz2 efg jkl abc3 EDF3 xyz3 
  efg jkl abc4 EDF2 xyz4 ... abc1 EDF1 \form{xyz1} efg jkl abc2 
  EDF2 \form{xyz2} efg jkl abc3 EDF3 \form{xyz3} efg jkl abc4 
  EDF2 \form{xyz4} ...}
\end{document}

enter image description here

If you need multi-layer parsing, say that efg jkl is the trigger to separate larger subgroups of arguments, then we have the following (note: efg jkl is not considered an argument, but an argument separator):

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{listofitems,tabto}
\newcommand\form[1]{\emph{#1}}
\newcommand\fun[1]{%
  \setsepchar{efg jkl/ }%
  \readlist*\funlist{#1}%
  \foreachitem\x\in\funlist[]{%
    \foreachitem\y\in\funlist[\xcnt]{%
    Group \xcnt{} sub-argument \ycnt{} is\tabto{2in}``\detokenize
      \expandafter\expandafter\expandafter{\funlist[\xcnt,\ycnt]}'': 
    \tabto{3.2in}\funlist[\xcnt,\ycnt]\par
  }}%
}
\begin{document}
\fun{abc1 EDF1 xyz1 efg jkl abc2 EDF2 xyz2 efg jkl abc3 EDF3 xyz3 
  efg jkl abc4 EDF2 xyz4 ... abc1 EDF1 \form{xyz1} efg jkl abc2 
  EDF2 \form{xyz2} efg jkl abc3 EDF3 \form{xyz3} efg jkl abc4 
  EDF2 \form{xyz4} ...}
\end{document}

enter image description here

  • Can you say how to get a sentence in one line to occur, rather than a list? Of the rightmost list in the image? (Also for learning how to write such macros, how does listofitems pull this off, can you suggest? I assume it checks \ifx style whether the argument separator occurs or not to decide to loop and relies on a \def that that takes one argument and has two cases ... Or calls a package with check for \ifempty?) – Guido Jorg Mar 22 at 22:36
  • @GuidoJorg To learn about the package: ctan.org/pkg/listofitems – Steven B. Segletes Mar 22 at 22:39
  • @GuidoJorg As to your request, to make a sentence of the right hand column is not really to parse the list at all, but just to typeset the argument. So could you clarify what you are asking? – Steven B. Segletes Mar 22 at 22:40
  • The result looking for is what would be printed had abc1 EDF1 \form{xyz1} efg jkl abc2 EDF2 \form{xyz2} efg jkl abc3 EDF3 \form{xyz3} efg jkl abc4 EDF2 \form{xyz4} ... been entered manually. When in fact \fun{abc1 EDF1 xyz1 efg jkl abc2 EDF2 xyz2 efg jkl abc3 EDF3 xyz3 efg jkl abc4 EDF2 xyz4 ...} was entered manually. Though the knowledge of how to parse the list completely is useful to know, as is the package, didn't know about it :) – Guido Jorg Mar 22 at 22:42
  • 1
    @GuidoJorg Please see my revision. – Steven B. Segletes Mar 23 at 1:14

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.