# Macro returning array with vertical line

I want to make a macro "mymacro" that returns an array with two rows. The macro has two variables and each variable is a string word composed with "b","o", "x", "|". These strings represent "blank", "\bigcirc", "\bigotimes", "vertical line partitioning one row". The number of columns is arbitrary and equals to the number of "b,o,x" in one of the two variables and each variable has the same number of columns. Note that "|" does not affect the number of the columns.

For example,

\mymacro{|bo|x|}{o|x|b}


should returns

and this code is following.

\documentclass[amsmath]{article}
%for vertical line
\def\vl{\hfil\kern\arraycolsep\vline\kern-\arraycolsep\hfilneg}

\begin{document}
$\begin{array}{cccc} \vl& & \bigcirc \vl & \bigotimes \vl \\\cline{2-4} &\bigcirc \vl& \bigotimes \vl & \end{array}$
\end{document}


This example is three columns case. I refereed to this question for the vertical line.

I tried to apply the answers of similar question, but I was not familar with Tex macro programing and could not solve the above problem.

Thank you for your cooperations.

This is extensible by adding character translations. It uses the standard array environment.

The idea is to change an input such as |bo|x| into

\multicolumn{1}{|c}{\__fukai_mymacro_char:n{b}} &
\multicolumn{1}{c|}{\__fukai_mymacro_char:n{o}} &
\multicolumn{1}{c|}{\__fukai_mymacro_char:n{x}}


where the last function translates b into a phantom, just to be sure it occupies the same space as the other characters, o into \bigcirc and x into \bigotimes.

The search regular expression means: find zero or one occurrences of | followed by a letter and zero or one occurrences of |. This is replaced by \multicolumn as shown above. At the end of this multiple replacement we need to remove an unwanted trailing &.

The number of columns is computed by examining the arguments and passing +1 for anything that's not |.

\documentclass{article}
\usepackage{array}

\ExplSyntaxOn

\NewDocumentCommand{\mymacro}{mm}
{
\fukai_mymacro:nn { #1 } { #2 }
}

\tl_new:N \l__fukai_mymacro_a_tl
\tl_new:N \l__fukai_mymacro_b_tl

\cs_new_protected:Nn \fukai_mymacro:nn
{
% normalize the input
\__fukai_mymacro_normalize:Nn \l__fukai_mymacro_a_tl { #1 }
\__fukai_mymacro_normalize:Nn \l__fukai_mymacro_b_tl { #2 }
% start the array with the suitable number of columns
\begin{array}
{
* { \int_max:nn { \__fukai_mymacro_count:n { #1 } } { \__fukai_mymacro_count:n { #1 } } } { c }
}
\tl_use:N \l__fukai_mymacro_a_tl
\\ \hline
\tl_use:N \l__fukai_mymacro_b_tl
\end{array}
}

\cs_new_protected:Nn \__fukai_mymacro_normalize:Nn
{
\tl_set:Nn #1 { #2 }
\regex_replace_all:nnN
{ (\|)??([[:alpha:]])(\|?) } % |<char>|
{ \c{multicolumn}\{1\}\{\1 c \3\}\{ \c{__fukai_mymacro_char:n}\{\2\} \} \& }
#1
\regex_replace_once:nnN { \& \Z } { } #1
}
% count the non-| characters
\cs_new:Nn \__fukai_mymacro_count:n
{
\int_eval:n { 0 + \str_map_function:nN { #1 } \__fukai_mymacro_ischar:n }
}
\cs_new:Nn \__fukai_mymacro_ischar:n
{
\str_if_eq:nnF { #1 } { | } { +1 }
}
% the translations
\cs_new:Nn \__fukai_mymacro_char:n
{
\str_case:nn { #1 }
{
{b}{\phantom{\bigcirc}}
{o}{\bigcirc}
{x}{\bigotimes}
}
}

\ExplSyntaxOff

\begin{document}

$\mymacro{|b|b|ooo|x|}{oooooo}$

\bigskip

$\mymacro{|bo|x|}{o|x|b}$

\bigskip

$\begin{array}{ccc} \multicolumn{1}{|c}{} & \multicolumn{1}{c|}{\bigcirc} & \multicolumn{1}{c|}{\bigotimes} \\\hline \multicolumn{1}{c|}{\bigcirc} & \multicolumn{1}{c|}{\bigotimes} & \multicolumn{1}{c}{} \end{array}$

\end{document}


In the image, the third array is “hand made” to show the result is the same as with the second array done with \mymacro.

• It's working! Thank you for the answer so many times! May I ask an additional question? Can I adjust the horizontal spacing, between "o" and "x" or "b" and "o" or "x" and "b" for example. I'd like the horizontal space between them to be a little narrower. Thanks, Jan 20 at 0:29
• I solved it myself. The array environment allows me to use \arraycolsep command, thank you! Jan 20 at 0:40

For example, you can try the following code. TeX primitives are used here.

\def\mymacro{\vbox\bgroup \mymacroA}
\def\mymacroA#1{\hbox{\mystrut\mymacroB#1\relax}\futurelet\next\mymacroE}
\def\mymacroB#1{\ifx\relax#1\else \mymacroC{#1}\expandafter\mymacroB\fi}
\def\mymacroC#1{\ifx b#1\mymacroD{}\fi
\ifx o#1\mymacroD{\bigcirc}\fi
\ifx x#1\mymacroD{\bigotimes}\fi
\ifx |#1\kern-.2pt\vrule\kern-.2pt\fi
}
\def\mymacroD#1{\hbox to2em{$\hss#1\hss$}}
\def\mymacroE{\ifx\next\bgroup\hrule\expandafter\mymacroA\else\egroup\fi}
\def\mystrut{\vrule height2ex depth1ex width0pt\relax}

%% test:
\mymacro{|bo|x|}{o|x|b}

\end

• It's working! The spacing is also easy to change. Thank you for the answer. May I ask an additional question? Considering $+\mymacro{|bo|x|}{o|x|b}$, the "+" is just adjacent to the lower row. Is it possible to place "+" adjacent to the middle of the two rows? Jan 20 at 0:22

This is an approach where the horizontal line between two rows of symbols is as long as the longer of the two rows:

\makeatletter
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\expandafter\UD@stopromannumeral\@secondoftwo}{%
\expandafter\UD@stopromannumeral\@firstoftwo}%
}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=\^^00}%

\newcommand\UD@symbolbox[1]{\vbox to2em{\vss\hbox to 2em{\hss#1\hss}\vss}}%

\@ifdefinable\UD@gobbletoexclam{\long\def\UD@gobbletoexclam#1!{}}%
\@ifdefinable\UD@replacefork{\long\def\UD@replacefork#1!|!b!o!x!#2#3!!!!{#2}}%
\@ifdefinable\UD@replaceloop{%
\long\def\UD@replaceloop#1#2#3\bizarre{%
\UD@CheckWhetherNull{#3}{#1{\phantom}\vrule}{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletoexclam#2!}{%
\UD@replacefork!#2!b!o!x!{\UD@replaceloop{\@gobble}}%
!|!#2!o!x!{#1{\phantom}\vrule\UD@symbolbox{$\hss$}\UD@replaceloop{\@firstofone}}%
!|!b!#2!x!{#1{\phantom}\vrule\UD@symbolbox{$\bigcirc$}\UD@replaceloop{\@firstofone}}%
!|!b!o!#2!{#1{\phantom}\vrule\UD@symbolbox{$\bigotimes$}\UD@replaceloop{\@firstofone}}%
!|!b!o!x!{\UD@replaceloop{#1}}%
!!!!%
}{\UD@replaceloop{#1}}#3\bizarre
}%
}%
}%
\@ifdefinable\UD@loopoverrows{%
\long\def\UD@loopoverrows#1#2#3#4#5\bizarre{%
%#1 - previous row
%#2 - separators in current iteration
%#3 - separators in consecutive iterations
%#4 - current row
%#5 - following rows
\UD@CheckWhetherNull{#5}{}{%
\@secondoftwo#2%
\vbox{%
\baselineskip=0pt \lineskip=0pt
\@firstoftwo#2%
\hbox{\hphantom{\UD@replaceloop{\@firstofone}#1{{}{}}\bizarre}}%
\hbox{\UD@replaceloop{\@firstofone}#4{{}{}}\bizarre}%
}%
\UD@loopoverrows{#4}{#3}{#3}#5\bizarre
}%
}%
}%
\newcommand\mymacro[1]{%
\vbox{\UD@loopoverrows{}{{}{}}{{\hrule}{\hrule height 0pt depth 0pt}}#1{{}{}}\bizarre}%
}%
\makeatother

\documentclass{article}
\begin{document}

\mymacro{
{|bo|x|}
{o|b|xo|b}
{x|o|b}
{o|x|b}
{o|x|b|oxbx}
{|x|b|x|}
}

\end{document}


Height and width of the rectangles with the o/b/x-thingies is coded in the macro \UD@symbolbox`. Height of horizontal rules /width of vertical rules is not included in the measurement of these rectangles. In case a vertical rule is not desired, that vertical rule is a phantom.