1

Long story short, I have been using \patchcommand within a \whiledo loop to try to construct a string piece-meal from an old string, inserting certain characters along the way.

I cut off a bit of the original \myText, and define it as \fragment and try to add the fragment onto the end a string macro called \myNewText, for a certain number of iterations.

The problem is, every time \fragment is redefined, all the previous additions I made to \myNewText change with it. I want to find a way to append the current value of \fragment to \myNewText so that \myNewText will not change whenever \fragment does.

Here's a MWE:

\documentclass[]{article}
\usepackage{ifthen}
\usepackage{patchcmd}
\usepackage{xstring}

\newcommand\myText{Hac est sententia.} \newcommand\myNewText{}

\newcommand\iterations{5} \newcounter{Completions}

\begin{document} \whiledo{\theCompletions < \iterations} { \stepcounter{Completions} \StrLeft{\myText}{\theCompletions}[\fragment] \patchcommand{\myNewText}{}{\fragment{'}}

\myNewText

} \end{document}

The code above produces:

H'
Ha'Ha'
Hac'Hac'Hac'
Hac 'Hac 'Hac 'Hac '
Hac e'Hac e'Hac e'Hac e'Hac e'

What I want to produce is:

H'
H'Ha'
H'Ha'Hac'
H'Ha'Hac'Hac
H'Ha'Hac'Hac 'Hac e'

Actually, what the final product I'm working towards is even more complex, but for the purposes of the MWE to illustrate my current problem, that'd do nicely.

fiat96
  • 165

3 Answers3

2

Sorry, but \patchcommand is not the best tool for the job.

Not knowing your real situation, I'll solve the problem you show.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\iterations}{mm} {% #1 = text, #2 = number of iterations \fiat_iteration:nn { #1 } { #2 } }

\tl_new:N \l__fiat_iteration_in_tl \tl_new:N \l__fiat_iteration_out_tl

\cs_new_protected:Nn \fiat_iteration:nn { % clear the token list containing the result \tl_clear:N \l__fiat_iteration_out_tl % store the input \tl_set:Nn \l__fiat_iteration_in_tl { #1 } % replace spaces by \space, for technical reasons \tl_replace_all:Nnn \l__fiat_iteration_in_tl { ~ } { \space } % execute some code from 1 to #2 \int_step_inline:nn { #2 } {% ##1 is the current index % append to the current list \tl_put_right:Nx \l__fiat_iteration_out_tl { % the initial segment of the input up to index ##1, followed by ' \tl_range:Nnn \l__fiat_iteration_in_tl { 1 } { ##1 } ' } % output the current list \tl_use:N \l__fiat_iteration_out_tl \par } }

\ExplSyntaxOff

\begin{document}

\iterations{Haec est sententia}{6}

\end{document}

enter image description here

egreg
  • 1,121,712
1

ifthen and xstring commands are not expandable and probably would not be my first choice here (even though I wrote the first of those) however you can expand \fragment before using it by re-arranging the call slightly

enter image description here

\documentclass[]{article}
\usepackage{ifthen}
\usepackage{patchcmd}
\usepackage{xstring}

\newcommand\myText{Hac est sententia.} \newcommand\myNewText{} \newcommand\helper[1]{\patchcommand{\myNewText}{}{#1{'}}} \newcommand\iterations{5} \newcounter{Completions}

\begin{document} \whiledo{\theCompletions < \iterations} {% dont forget comments at eol!! \stepcounter{Completions}% \StrLeft{\myText}{\theCompletions}[\fragment]% \expandafter\helper\expandafter{\fragment}%

\myNewText

} \end{document}

David Carlisle
  • 757,742
1

David Carlisle already proposed passing (via \expandafter) the toplevel-expansion of \fragment to an auxiliary macro called \helper.

The purpose of this auxiliary macro is rather specific.

A more general variation of this approach is combining usage of \expandafter (for obtaining \fragment's toplevel-expansion) with usage of a macro \exchange which does exchange two arguments.

(\helper can be used for one purpose only. The \expandafter-\exchange-technique can be used (even nested) in many situations without the need of defining additional macros.)

\documentclass{article}
\usepackage{ifthen}
\usepackage{patchcmd}
\usepackage{xstring}

\newcommand\exchange[2]{#2#1}

\newcommand\myText{Hac est sententia.} \newcommand\myNewText{}

\newcommand\iterations{5} \newcounter{Completions}

\begin{document} \whiledo{\theCompletions < \iterations}% {% \stepcounter{Completions}% \StrLeft{\myText}{\theCompletions}[\fragment]% \expandafter\exchange\expandafter{\expandafter{\fragment{'}}}{\patchcommand{\myNewText}{}}%

\myNewText

} \end{document}

enter image description here

In case \unexpanded of the e-TeX-extensions is available, you can do something like this:

\documentclass[]{article}
\usepackage{ifthen}
\usepackage{patchcmd}
\usepackage{xstring}

\newcommand\myText{Hac est sententia.} \newcommand\myNewText{} \newcommand\iterations{5} \newcounter{Completions}

\begin{document} \whiledo{\theCompletions < \iterations}% {% \stepcounter{Completions}% \StrLeft{\myText}{\theCompletions}[\fragment]% \begingroup \edef\myNewText{\noexpand\patchcommand{\noexpand\myNewText}{}{\unexpanded\expandafter{\fragment}{'}}}% \expandafter\endgroup\myNewText

\myNewText

}% \end{document}

enter image description here

Btw: In some answers i saw "haec" instead of "hac". Afaik that does make a subtle difference. ;-)

Ulrich Diez
  • 28,770
  • This will help apply the function I need in other situations, thanks! Re. haec vs. hac: it looks like my off-the-cuff Latin grammar needs some polishing! – fiat96 Jul 10 '20 at 13:25