4

One of the issues I have with ams alignment environments is that they use "cells" which change in size to fit their content. This is required for all cells which are used for alignment, but I feel it is not really required for the last cell in each row (even if that row contains fewer cells). I also don't like the lr alignment and the extra spacing that align adds. All of this causes unintuitive spacing (for me), which I always struggle with. I decided to write a custom environment that behaves like I would expect.

The custom environment should treat the input as a block. Within this block, & symbols are treated as alignment points with no additional spacing added. When these & symbols are encountered, the effect (and the only effect) is that any text appearing after them will be aligned. For example here:

AAAAAAAAAAA \\
BB & C \\
EEEEEE & FFFFF \\

The first line does not affect alignment at all. The second line "defines" an alignment point which is not used up to here (in particular, the first line does not change the alignment here). The third line causes the left column to increase in size so that C and F are aligned.

I don't claim that this is impossible to achieve with ams alignment. However, adding the necessary &, math*lap and nested aligned commands is always a mystery to me, so I wanted something simple.

I figured out how to use a combination of array and multicolumn to achieve what I want. Essentially, I used the extended array package to default to >{{}}l<{{}} for each column (the {} are to fix spacing of binary operators) and to have the last column in every row span all remaining columns. I am not familiar enough with array or TeX to do this properly, so I instead used my limited knowledge of expl3 to add the necessary syntax to a more intuitive text. The result is in the following MWE, which I would like to solicit comment/feedback on.

In particular, is there already a way to achieve what I want without a custom environment? Or a better way than using array in my custom environment? Also, is my expl3 code correct or can it be improved? (I didn't figure out how to make it so that it works if I replace all the equations with a command that expands into the equations)

\documentclass{article}
\usepackage{array}
\usepackage{mathtools}

\ExplSyntaxOn

% ==== Variables for alignblock ====
\int_new:N \l_alignblock_col_max_int
\int_new:N \l_alignblock_row_cols_int
\int_new:N \l_alignblock_col_gap_int

\seq_new:N \l_alignblock_rows_seq
\seq_new:N \l_alignblock_cells_seq

\tl_new:N \l_alignblock_build_tl
\tl_new:N \l_alignblock_colspec_tl

% ==== alignblock environment ====
\NewDocumentEnvironment{alignblock}{O{}+b}
 {
   % Split rows
   \seq_set_split:Nnn \l_alignblock_rows_seq { \\ } {#2}

   % Compute max column count
   \int_zero:N \l_alignblock_col_max_int
   \seq_map_inline:Nn \l_alignblock_rows_seq
   {
     \seq_set_split:Nnn \l_alignblock_cells_seq { & } { ##1 }
     \int_set:Nn \l_alignblock_col_max_int
     { \int_max:nn { \l_alignblock_col_max_int } % { \l_alignblock_row_cols_int }
       { \seq_count:N \l_alignblock_cells_seq }}}

   % Array body
   \tl_build_begin:N \l_alignblock_build_tl
   \tl_build_put_right:Nn \l_alignblock_build_tl {
     \begingroup\everymath{\displaystyle}\setlength{\arraycolsep}{0pt}}

   \tl_set:Nx \l_alignblock_colspec_tl
   { \tl_if_blank:nTF {#1}
     { r*{\int_use:N \l_alignblock_col_max_int}{>{{}}l<{{}}} }
     { #1*{\int_use:N \l_alignblock_col_max_int}{>{{}}l<{{}}} }
   }

   \tl_build_put_right:Nx \l_alignblock_build_tl {
     \begin{array}[t]{\l_alignblock_colspec_tl}}

   \seq_map_indexed_inline:Nn \l_alignblock_rows_seq % Loop over lines
     {
       \seq_set_split:Nnn \l_alignblock_cells_seq { & } { ##2 }
       \int_set:Nn \l_alignblock_row_cols_int { \seq_count:N \l_alignblock_cells_seq }
       \int_set:Nn \l_alignblock_col_gap_int
         { \l_alignblock_col_max_int - \l_alignblock_row_cols_int + 1 }

       \seq_map_indexed_inline:Nn \l_alignblock_cells_seq % Loop over cells
         {
           \int_compare:nNnTF { ####1 } < { \l_alignblock_row_cols_int }
             { \tl_build_put_right:Nn \l_alignblock_build_tl { ####2 & } }
             {
               \int_compare:nNnTF { \l_alignblock_col_gap_int } = { 1 }
                 { \tl_build_put_right:Nn \l_alignblock_build_tl { ####2 } }
                 {
                   \tl_build_put_right:Nx \l_alignblock_build_tl
                     { \exp_not:N\multicolumn{ \int_use:N \l_alignblock_col_gap_int }{>{{}}l<{{}}} }
                   \tl_build_put_right:Nn \l_alignblock_build_tl { { ####2 } }}}}
       \int_compare:nNnT { ##1 } < { \seq_count:N \l_alignblock_rows_seq }
         { \tl_build_put_right:Nn \l_alignblock_build_tl { \\[\jot] } }}

   \tl_build_put_right:Nn \l_alignblock_build_tl { \end{array}\endgroup }
   \tl_build_end:N \l_alignblock_build_tl
 % \par\noindent\ttfamily  % DEBUG
 %   \detokenize\expandafter{\l_alignblock_build_tl}
 %   \par\normalfont
   \tl_use:N \l_alignblock_build_tl}{}

% ==== Variables for multlineR ====
\seq_new:N \l_multlineR_lines_seq
\tl_new:N  \l_multlineR_first_tl
\tl_new:N  \l_multlineR_last_tl
\tl_new:N  \l_multlineR_body_tl

% ==== multlineR environment ====
\NewDocumentEnvironment{multlineR}{s O{} +b}
 {
   \seq_set_split:Nnn \l_multlineR_lines_seq { \\ } { #3 }
   \seq_pop_left:NN  \l_multlineR_lines_seq \l_multlineR_first_tl
   \seq_pop_right:NN \l_multlineR_lines_seq \l_multlineR_last_tl
   \tl_clear:N \l_multlineR_body_tl

   % first line
   \tl_put_right:NV \l_multlineR_body_tl \l_multlineR_first_tl
   \tl_put_right:Nn \l_multlineR_body_tl { \\ \begin{alignblock}[#2] }

   % middle lines
   \seq_map_inline:Nn \l_multlineR_lines_seq
     { \tl_put_right:Nn \l_multlineR_body_tl { &{} ##1 \\ } }

     % last line
   \tl_put_right:Nn \l_multlineR_body_tl { &{} }
   \tl_put_right:NV \l_multlineR_body_tl \l_multlineR_last_tl
   \tl_put_right:Nn \l_multlineR_body_tl { \end{alignblock} }

   % wrap in multline or multline*
   \IfBooleanTF{#1}
     { \begin{multline*}\l_multlineR_body_tl\end{multline*} }
     { \begin{multline}\l_multlineR_body_tl\end{multline} }
   }{}

\NewDocumentEnvironment{multlineR*}{+b}
 { \multlineR*{#1} }{}
\ExplSyntaxOff


\begin{document}
\textbf{alignblock} vs \textbf{align} example usage (same input):
\[
  \begin{alignblock}
    \textnormal{\textbf{alignblock}}\\
    AB &= a+b+c+d+e \\
    &= a  & + b+WW &+d \\
    &     & + o+q  &+r \\
    &= aB & +o+p+q+r \\
    &= a+b+c+d+e \\
    &= a  & +b     &+e \\
    &     &+ c+d+e
  \end{alignblock}\hskip1cm
  % A (useless) comparision
  \begin{aligned}[t]
    \textnormal{\textbf{aligned}}\\
    AB &= a+b+c+d+e \\
    &= a  & + b+WW &+d \\
    &     & + o+q  &+r \\
    &= aB & +o+p+q+r \\
    &= a+b+c+d+e \\
    &= a  & +b     &+e \\
    &     &+ c+d+e
  \end{aligned}
\]

\textbf{multlineR} example usage:
\begin{multlineR*}
  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\
    &= a  & + b+WW &+d \\
    &     & + o+q  &+r \\
    &= aB & +o+p+q+r \\
    &= a+b+c+d+e \\
    &= a  & +b     &+e \\
    &     &+ c+d+e
\end{multlineR*}
\end{document}

aligned vs alignbox

13
  • 1
    the spacing in align is because it is not designed for multiple alignment points in an expression but for multiple equations per line a=b c=d you need space between the equations. use alignat for alignment points with no added space. Commented Oct 13 at 20:27
  • your test expression is rather odd with no left hand side, the expression starts with = ??? (I would show a version with alignat but not really sure if you really intend the first equation to start with & and have nothing=something Commented Oct 13 at 20:31
  • 1
    The system is warning about too many comments but you have no question in this post it does not really fit the site format. The question should probably be closed unless you can edit it so that it can have an answer. Commented Oct 13 at 22:02
  • 3
    [code-review] is on-topic (and kind of a special case), so I've added that since it seems to fit the question. Feel free to revert if that part wasn't your “main” question. Commented Oct 13 at 23:17
  • 1
    You might get some useful ideas from this question: tex.stackexchange.com/q/50483 Commented Oct 15 at 23:44

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.