1

I wish to use the code below in order to get the maximum length of a list of words.

\documentclass[border=5mm]{standalone}
\usepackage{tikz}
\usepackage{calc}
\begin{document}
\begin{tikzpicture}
 \def\words{%
   xxx,
   xxxxxx,
  xxxxxxxxx%
 }
\newlength{\mywidth}
\setlength\mywidth{0pt}
 \foreach \word in \words {%
     \setlength\mywidth{\maxof{\mywidth}{\widthof{word}}}
     \message{showinfo1 \word -> \the\mywidth};
  }
\message{showinfo2  \the\mywidth};
\end{tikzpicture}
\end{document}

But output results looks like weird:

showinfo1 xxx-> 21.4167pt
showinfo1 xxxxxx-> 21.4167pt
showinfo1 xxxxxxxxx-> 21.4167pt
showinfo2 0.0pt
  1. The showinfo1 should be increase one by one since word length increase, but actual output the same value
  2. The showinfo2 should be the last valid value but it show as 0.

Any global version should I use?

Qrrbrbirlbel
  • 119,821
lucky1928
  • 4,151
  • 3
    You use word as an argument. That's always the same length. You probably wanted \word. A PGFFor scopes its body but the \setlength assignment is local and will be gone after the loop. You will need to use some form of loop that doesn't scope its body, some form of \gsetlength or a different approach. (Since you're already using TikZ you could use \pgfmathsetlength\mywidth{max(width("xxx"),width("xxxxxx"),…))} or \pgfkeys{max length/.code=\setlength\mywidth{\maxof{\mywidth}{\widthof{#1}}}, max length/.list/.expand once=\words}. – Qrrbrbirlbel Nov 26 '23 at 03:10

2 Answers2

1

You always get the same because you're measuring “word” and not (the expansion of) \word.

However the main problem is that \foreach executes every step of the loop in a group, so the setting to \mywidth gets forgotten and, indeed, you get zero at the end. You need a loop that doesn't use grouping.

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\settomaxwidth}{mm} {% #1 = length parameter % #2 = list of words \lucky_settomaxwidth:Nn #1 { #2 } }

\dim_new:N \l__lucky_tempwd_dim

\cs_new_protected:Nn \lucky_settomaxwidth:Nn { % start by setting the width to zero \dim_zero:N \l__lucky_tempwd_dim % map the given list \clist_map_inline:nn { #2 } { % set the current word in a box \hbox_set:Nn \l_tmpa_box { ##1 } % set the temporary length to the max of the two \dim_set:Nn \l__lucky_tempwd_dim { \dim_max:nn { \box_wd:N \l_tmpa_box } { \l__lucky_tempwd_dim } } } % finalize \dim_set_eq:NN #1 \l__lucky_tempwd_dim }

\ExplSyntaxOff

\newlength{\mywidth}

\settomaxwidth{\mywidth}{ xxx, xxxxxx, xxxxxxxxx }

\typeout{Max width: \the\mywidth}

\settomaxwidth{\mywidth}{xxx,xxxxxx,xxxxxxxxx}

\typeout{Max width: \the\mywidth}

\end{document}

The two examples show that spaces around the commas are ignored.

Max width: 47.50021pt
Max width: 47.50021pt
egreg
  • 1,121,712
1

The mistake about word instead \word and the fact that \foreach from TikZ runs its body in a group was mentioned in another answer.

The egerg's answer here shows solution based on expl3. I can show another two answers:

Based on TeX primitives:

\newdimen\mywidth

\def\measureword #1,{% \ifx \end#1 \relax \else \setbox0 = \hbox{\ignorespaces #1}% \ifdim\wd0>\mywidth \mywidth=\wd0 \fi \expandafter \measureword \fi }

\def\words{% xxx, xxxxxx, xxxxxxxxx% } \expandafter\measureword \words,\end,

\message{Result: \the\mywidth}

In OpTeX:

\def\words{%
   xxx,
   xxxxxx,
  xxxxxxxxx%
 }

\replstring\words{, }{,} \ea\foreach \words,\do #1,{\setbox0=\hbox{#1}\ifdim\wd0>\mywidth \mywidth=\wd0 \fi}

\message{Result: \the\mywidth} \bye

wipet
  • 74,238