4

I've done lots of programming in other languages but very new to LaTeX.

Best to refer to the document below:

\documentclass{book}
\usepackage{xparse}

\begin{document}

Version 1

\begin{tabular}{| c | c | c | c | c |}
  \hline            
  A & B & C & D & E \\\hline            
    & G & H &   &   \\\hline            
\end{tabular}
\\
\\

\ExplSyntaxOn
\NewDocumentCommand{\stringprocess}{ m m }
 {
  \egreg_string_process:nn { #1 } { #2 }
 }
\cs_new_protected:Npn \egreg_string_process:nn #1 #2
 {
  \tl_map_inline:nn { #2 } { #1 { ##1 } }
 }
\ExplSyntaxOff

\newcommand{\boxchar}[1]{#1 &}

\newcommand{\boxwords}[2]{
\begin{tabular}{| c | c | c | c | c | c | c | c | c |}
  \hline            
  \stringprocess{\boxchar}{#1}
  \\\hline
  \stringprocess{\boxchar}{#2}
  \\\hline  
\end{tabular}
}

Version 2

\boxwords{ABCDE}{ GH  }
\\
\\

Version 3

\boxwords{ABCDE}{.GH..}
\\
\\

\end{document}

enter image description here

Version 1 comes out as I would want it. But I want to create a new command to allow easier syntax. Rather than type all the individual letters I want to just give the two words. So I want to just type: \boxwords{ABCDE}{.GH..} to produce the same boxed words.

My first attempt is Version 2.

Issue A: There's an extra column on the end with blank. I understand that this comes from the trailing & on the last letter of each word. In other programming languages I'd know how to suppress this last & at the end of the loop but not sure how to do this in Latex?

Issue B: I don't want the dots to appear. I just wanted them to be placeholders in the input to the function so that they come out as spaces.

I tried just putting the spaces in directly in Version 3 but rather than cause empty boxes, the spaces cause boxes to be missed out.

4

The key is \seq_use:Nn for producing the table rows. First we set sequences containing the two items (after changing . into \relax), make their length equal and print the table.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\boxwords}{mm}
 {
  \suhada_boxwords:nn { #1 } { #2 }
 }

\seq_new:N \l_suhada_boxwords_a_seq
\seq_new:N \l_suhada_boxwords_b_seq
\tl_new:N \l_suhada_boxwords_temp_tl

\cs_new_protected:Nn \suhada_boxwords:nn
 {
  % replace . with \scan_stop: in the first argument
  % and set the first sequence
  \tl_set:Nn \l_suhada_boxwords_temp_tl { #1 }
  \tl_replace_all:Nnn \l_suhada_boxwords_temp_tl { . } { \scan_stop: }
  \seq_set_split:NnV \l_suhada_boxwords_a_seq { } \l_suhada_boxwords_temp_tl
  % replace . with \scan_stop: in the first argument
  % and set the first sequence
  \tl_set:Nn \l_suhada_boxwords_temp_tl { #2 }
  \tl_replace_all:Nnn \l_suhada_boxwords_temp_tl { . } { \scan_stop: }
  \seq_set_split:NnV \l_suhada_boxwords_b_seq { } \l_suhada_boxwords_temp_tl
  % make the same number of items in both sequences
  \int_compare:nT
   {
    \seq_count:N \l_suhada_boxwords_a_seq < \seq_count:N \l_suhada_boxwords_b_seq
   }
   {
    \prg_replicate:nn
     {
      \seq_count:N \l_suhada_boxwords_b_seq - \seq_count:N \l_suhada_boxwords_a_seq
     }
     {
      \seq_put_right:Nn \l_suhada_boxwords_a_seq { \scan_stop: }
     }
   }
  \int_compare:nT
   {
    \seq_count:N \l_suhada_boxwords_b_seq < \seq_count:N \l_suhada_boxwords_a_seq
   }
   {
    \prg_replicate:nn
     {
      \seq_count:N \l_suhada_boxwords_a_seq - \seq_count:N \l_suhada_boxwords_b_seq
     }
     {
      \seq_put_right:Nn \l_suhada_boxwords_b_seq { \scan_stop: }
     }
   }
  % print the table
  \begin{tabular}{|*{\seq_count:N \l_suhada_boxwords_a_seq}{c|}}
  \hline
  \seq_use:Nn \l_suhada_boxwords_a_seq { & } \\
  \hline
  \seq_use:Nn \l_suhada_boxwords_b_seq { & } \\
  \hline
  \end{tabular}
 }
\ExplSyntaxOff

\begin{document}

\boxwords{ABCDE}{.GH}

\bigskip

\boxwords{ABCDE}{.GH..}

\bigskip

\boxwords{ABC..}{.GH.I}

\end{document}

enter image description here

  • Thanks egreg for your reply - works really well. I like your defensive programming style - for instance allowing for strings to be different length. – Adahus Aug 15 '16 at 12:16
  • The line: \tl_replace_all:Nnn \l_suhada_boxwords_temp_tl { . } { \scan_stop: } solves my problem of replacing the dots. I also want to replace some other characters. I want to replace * with the multiply symbol. To test the principle I tried adding: \tl_replace_all:Nnn \l_suhada_boxwords_temp_tl { * } { m } and that works fine. I tried: \tl_replace_all:Nnn \l_suhada_boxwords_temp_tl { * } { $\times$ } but I get an error: Extra }, or forgotten $. Can't see what is wrong? – Adahus Aug 15 '16 at 12:54
  • @Suhada You need braces around $\times$, so it is considered a single item when the splitting takes place: \tl_replace_all:Nnn \l_suhada_boxwords_temp_tl { * } { {$\times$} } – egreg Aug 15 '16 at 12:57
2

Expl3 has booleans which can be used similar to the bools from other languages. To create the empty boxes, you can use {} for empty groups.

\documentclass{book}
\usepackage{xparse}

\begin{document}
\ExplSyntaxOn
\bool_new:N\l_is_first_bool
\NewDocumentCommand{\stringprocess}{ m m }
 {
   \bool_set_true:N\l_is_first_bool
   \tl_map_inline:nn{#2}{#1{##1}\l_is_first_bool\bool_set_false:N\l_is_first_bool}
 }

 \newcommand{\boxchar}[2]{\bool_if:NTF#2{}{&}#1}
\ExplSyntaxOff

\newcommand{\boxwords}[2]{
\begin{tabular}{| c | c | c | c | c | c | c | c | c |}
  \hline            
  \stringprocess{\boxchar}{#1}
  \\\hline
  \stringprocess{\boxchar}{#2}
  \\\hline  
\end{tabular}
}

Version 2

\boxwords{ABCDE}{{}GH{}{}}
\end{document}
  • Thanks zauguin for your reply - helps me understand more about how latex programming works. I've decided to go with the sequence approach as it fits with my further development. – Adahus Aug 15 '16 at 12:15

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.