8

I want to split a string into individual characters, transform each character individually, then join them back together.

Something like this:

\newcommand{\drawaboxaroundit}[1]{...}
\newcommand{\glue}{+}
\explodethenjoin{abcdef}{\drawaboxaroundit}{\glue}

Glossing over how \drawaboxaroundit might work (it's just for illustration, it's not what I'm really doing), the result for this example would be something like this:

enter image description here

The intention is for \explodethenjoin{abc}{\drawaboxaroundit}{\glue} will expand to

\drawaboxaroundit{a}\glue\drawaboxaroundit{b}\glue\drawaboxaroundit{c}

I nearly have a solution to my problem with this answer (although I know basically nothing about expl so I don't understand how it works) -- this processes each character individually but does not put glue between them.

One approach (which doesn't seem like good design to me) would be to make \drawaboxaroundit aware of its position, i.e. the above abc example would expand to

\drawaboxarounditwrapper{0}{\glue}{a}\drawaboxarounditwrapper{1}{\glue}{b}\drawaboxarounditwrapper{2}{\glue}{c}

with \drawaboxarounditwrapper defined to be something like this

\newcommand{\drawaboxarounditwrapper}[2]{
    \ifthenelse{\equal{#1}{0}}{}{\glue}
    \drawaboxaroundit{#2}
}

Another option would be to split the string into head character and tail string. Something like this

\witheachcharacterinheadandtail{abcdef}{\drawaboxaroundit}{\glue\drawaboxaroundit}

Where \witheachcharacterinheadandtail{abcdef}{\foo}{\bar} expands into

\foo{a}\bar{b}\bar{c}\bar{d}\bar{e}\bar{f}

This second approach seems to be better, but I don't know how to realize either of these approaches, or whether either is a good idea in the first place.

How can I explode-transform-join a string? We can assume the strings in question are ASCII and contain no latex commands, and all strings will contain at least two characters (a tail will always have nonzero length).

spraff
  • 1,501
  • How about spaces? Are these allowed or not? – Joseph Wright Apr 12 '17 at 21:11
  • @wipet's answer to http://tex.stackexchange.com/questions/233085/basics-of-parsing/233227#233227 shows how to do basic character parsing in TeX. I used this to make a command that puts each character in a different rainbow color here: http://tex.stackexchange.com/questions/331733/is-there-something-similar-to-or-compatible-with-latex-that-supports-more-of-a-c/331764#331764 . You could just change \scando to do what you want instead of \spectrum. – musarithmia Apr 12 '17 at 22:00

3 Answers3

9

enter image description here

\documentclass{article}
\def\xloop#1{\ifx\relax#1\else\xloopbody{#1}\expandafter\xloop\fi}
\newcommand\explodethenjoin[3]%
  {\def\inter{\def\inter{#3}}%
   \def\xloopbody##1{\inter#2{##1}}%
   \xloop#1\relax
  }
\begin{document}
\explodethenjoin{abcdef}{\fbox}{+}
\end{document}

The macro \xloop iterates over the tokens following it until it encounters \relax. It has been used several times on this site. For each token the macro \xloopbody is executed; by defining it appropriately, the tokens can be processed as needed.

gernot
  • 49,614
6

As TeX doesn't really have strings, you do not need any specific commands to split up a token list, it is already a list of distinct tokens. Similarly you don't have to use an explicit loop macro as iterating over a token list is just the normal tex behaviour.

\documentclass{article}

\def\zz#1{\def\zzsep{}\zzz#1\relax}
\def\zzz#1{\ifx\relax#1\else\zzsep\def\zzsep{+}\fbox{#1}\expandafter\zzz\fi}
\begin{document}
\zz{abcdef}
\end{document}

enter image description here

David Carlisle
  • 757,742
  • Isn't this exactly my solution (except for the wrapper and renaming)? And I'd call \zzz a loop since it calls itself ... Of course, my solution should maybe be better called your solution as the code is attributed to you in one post, or maybe to someone else since similar code appears in various style files and classes. – gernot Apr 13 '17 at 11:21
  • @gernot yes more or less, I probably just concentrated on making sure egreg didn't get a tick:-) (didn't notice this comment at the time for some reason) – David Carlisle May 27 '17 at 10:00
  • While two dogs are fighting for a bone, a third runs away with it ;-) – gernot May 28 '17 at 09:13
5

It's a breeze with xparse:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\splitstring}{mmO{}}
 {
  \spraff_string_split:nnn { #1 } { #2 } { #3 }
 }

\seq_new:N \l_spraff_string_in_seq
\seq_new:N \l_spraff_string_out_seq

\cs_new_protected:Nn \spraff_string_split:nnn
 {
  % split the string at 'nothing'
  \seq_set_split:Nnn \l_spraff_string_in_seq { } { #1 }
  % change each item into '#2{<item>}'
  \seq_set_map:NNn \l_spraff_string_out_seq \l_spraff_string_in_seq { #2 { ##1 } }
  % use the sequence with '#3' in between items
  \seq_use:Nn \l_spraff_string_out_seq { #3 }
 }
\ExplSyntaxOff

\NewDocumentCommand{\boxit}{m}{\fbox{\strut#1}}

\begin{document}

\splitstring{abcdef}{\boxit}

\splitstring{abcdef}{\boxit}[${}+{}$]

\end{document}

Beware that the macro used as second argument to \splitstring should be defined with \NewDocumentCommand.

enter image description here

egreg
  • 1,121,712