Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

In my work with linear algebra and matrices I have been quite restricted by LaTeX2 and its maximum of 9 arguments that may be passed to a \newcommand. While LaTeX3 imposes that same limitation for the \NewDocumentCommand I noticed (with considerable excitement) that the xparse package provides a number of argument processors which will work around this limitation. In particular \SplitArgument appears to offer just what I need. Regrettably I am quite unable to extract enough information from my reading of the interface3 documentation to write the layer that will use the data extracted by \SplitArgument and put them where I need them. The code below I hope is self-explanatory; it compiles for \myNinePerm but does not compile for mynPerm for obvious reasons. I am sure an answer to my problem would not only be a great help to me but serve all newcomers to TeX and LaTeX3 as a welcome practical example.

%document name: LaTeX3_xparse2.tex
%RN 16/3/2012
%COMMENTS: 
%   OBJECTIVE: Want to populate an nxn matrix by passing its elements separated by semicolons
%   as a single argument rather than by passing them individually (which poses the restriction 
%   to 3 x 3 matrices since LaTeX3 imposes the same maximum number of arguments, 9, as does 
%   LaTeX2). LaTeX3 and one or other of the argument processors in xparse, for instance 
%   \SplitArgument will to the job, but once again, due to the lack of at least one concrete 
%   example, I cannot get the syntax right, especially for the internal function! 
%   APPROACH: 
%   (1) Have a simple function, call it \myNinePerm which displays a permutation of a 9-set 
%   as a 2 x 9 matrix with the top row showing the elements of the set in natural order and 
%   row 2 allowing the user to plug in their images under the mapping as individual arguments 
%   by passing the values as arguments(the LaTeX2 approach);
%   (2) rewrite this same function by passing a single argument which contains the values 
%   separated by some token, for example a semi-colon. The restriction to nine elements is no 
%   longer relevant, but is maintained in order to avoid clouding the issue.
%==============================================================================================
\documentclass{article}
%\usepackage{expl3}
\usepackage{amsmath,xparse} 

\ExplSyntaxOn 
\NewDocumentCommand{\myNinePerm}{ m m m m m m m m m } 
{ 
    {\begin{pmatrix} 
    1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \\
    #1 & #2 & #3 & #4 & #5 & #6 & #7 & #8 & #9 
    \end{pmatrix}} 
}
\ExplSyntaxOff 

\ExplSyntaxOn 
\NewDocumentCommand{\mynPerm}{ m } 
{ > { \SplitArgument { 8 } { ; } } m }
% assuming that the argument now contains nine values, how do I move these values to
% occupy the second row in the matrix below?
    { 
    {\begin{pmatrix} 
    1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \\
%   <val 1>&<val 2>&<val 3>&<val 4>&<val 5>&<val 6>&<val 7>&<val 8>&<val 9> 
    \end{pmatrix}} 
}
\ExplSyntaxOff 

\begin{document}
$\myNinePerm{9}{8}{7}{6}{5}{4}{3}{2}{1}$\\
$\mynPerm{{9;8;7;6;5;4;3;2;1}}$\\
\end{document}
share|improve this question
1  
Wouldn't it be a better idea to use some kind of a key-value syntax (or comma-delimited lists)? –  mbork Mar 16 '12 at 10:14
    
You tell me. I am open to suggestions. I am a complete novice to the LaTeX game and the existing LaTeX3 documentation, at least what I was able to extract out of my MiKTeK2.9 release, is entirely abstract. Not a single worked concrete example in sight. So, if you were to provide me with key-value syntax approach solution to my little assignment, this would go a long way helping me benefit from reading chapter XX in the interface3 document. At the same time, inferior or not, I would still like to grasp the detail of the \SplitArgument approach. –  Reinhard Neuwirth Mar 16 '12 at 11:54
    
You seem to have an extra set of braces in the argument of \mynPerm, don't you? –  Bruno Le Floch Mar 17 '12 at 10:01
    
Correct. A very pathetic attempt at being a LaTeX3 programmer indeed. You will notice I have had 2 valid answers and accepted one of them for its shortness and elegance. I am currently engaged in reading the LaTeX3 documentation to find out how the code does what it does, in order to adopt this answer, or the other, for general use. You may agree that the existing LaTeX3 documentation is written by the developers for experts and other developers; not the kind of documentation newcomers find easy to learn from. For me, concrete, fully worked examples which compile, are the key. –  Reinhard Neuwirth Mar 17 '12 at 10:56
add comment

2 Answers

up vote 4 down vote accepted

\SplitArgument and \SplitList are for very elementary list processing. When one has to insert something only between the items the things are slightly more complicated.

I propose a simpler version that copes also with an arbitrary number of elements

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\perm}{ O{,} m } { \perm_build:nn { #1 } { #2 } }

%%% Here's the "real" code
\tl_new:N \l_perm_toprow_tl
\tl_new:N \l_perm_bottomrow_tl
\seq_new:N \l_perm_permutation_seq
\int_new:N \l_perm_numberofcols_int
\cs_new:Npn \perm_build:nn #1 #2
  {
   \group_begin:
   \cs_set_eq:cN {c@MaxMatrixCols} \l_perm_numberofcols_int
   \seq_set_split:Nnn \l_perm_permutation_seq { #1 } { #2 }
   \int_set:Nn \l_perm_numberofcols_int { 1 }
   \tl_set:Nn \l_perm_toprow_tl { 1 }
   \seq_pop_left:NN \l_perm_permutation_seq \l_perm_bottomrow_tl
   \seq_map_inline:Nn \l_perm_permutation_seq
     {
      \int_incr:N \l_perm_numberofcols_int
      \tl_put_right:Nx \l_perm_toprow_tl { & \int_to_arabic:n { \l_perm_numberofcols_int } }
      \tl_put_right:Nn \l_perm_bottomrow_tl { & ##1 }
     }
   \begin{pmatrix}
   \tl_use:N \l_perm_toprow_tl \\
   \tl_use:N \l_perm_bottomrow_tl
   \end{pmatrix}
   \group_end:
  }
\ExplSyntaxOff
\begin{document}
$\perm{1,2,3,4,5,6,7,8,9}$

$\perm[;]{9;8;7;6;5;4;3;2;1;10;11;12}$
\end{document}

It uses a sequence for doing the usual mapping after having detached the first column. At each step of the mapping we add the appropriate element to the top and bottom rows.

Finally we set a pmatrix, but with the trick of having let \c@MaxMatrixCols to \l_perm_numberofcols_int so that LaTeX won't groan when more columns than the default 10 are requested.

How it works

We first define \perm with an optional value (default ,) and a mandatory one, the second row of the matrix we want to build. This just transfers control to the "inner" version \perm_build:nn.

The next step is to declare the variables: two token lists for storing the rows, a sequence for the hard work and an integer that will perform two services.

In a group we make LaTeX believe that \c@MaxMatrixCols is our integer variable (see the documentation of amsmath for MaxMatrixCols) and then we use the optional argument to \perm for splitting the mandatory argument and store the pieces of information in the sequence.

Then we start a recursion on the sequence, after initializing the integer variable to 1 and the top row to contain "1" (this is necessary to avoid a row separator & too much). So we detach the leftmost element in the sequence (\seq_pop_left:NN) initializing the bottom row (thanks to Bruno Le Floch for this clever trick).

With \seq_map_inline:Nn we do the recursion: for each element of the sequence we add to the top row the (just incremented) value of the integer variable preceded by &; similarly, we add to the bottom row & followed by the current item in the sequence (represented by ##1).

Finally we set the matrix and close the group, so leaving MaxMatrixCols untouched, without having restrictions on the number of columns during our job.

This might be generalized to having in the top row an arbitrary sequence of elements, by using the function \seq_mapthread_function:NNN.

share|improve this answer
    
Your code compiles without any problems, and behaves perfectly. It produces the correct result for a variety of separators, for example <space> (ASCII 32) and for any size n. The only concern is that it highlights how much I have yet to learn, and I will have to study the interface3 manual thoroghly before I understand what your code does. I intend to adapt your code for a rewrite of all my matrix routines, which are currently in LaTeX2 and woefully inadequate. Thank you for your effort which I am marking as the accepted answer. –  Reinhard Neuwirth Mar 17 '12 at 3:27
1  
@ReinhardNeuwirth I've tried to explain in greater detail how the code works. –  egreg Mar 17 '12 at 10:51
    
Thank you, very helpful and much appreciated. –  Reinhard Neuwirth Mar 18 '12 at 1:25
add comment

I want to try to answer your question and provide an approach.

First a small hint: The commands provided by xparse don't need to surround with ExplSyntaxOn ... ExplSyntaxOff

In the approach above the function \mynPerm is defined with one optional argument and one mandatory argument. The syntax is as follows:

\mynPerm[<separator>]{<list>}

The default separator is ;. The input argument are not limited. The first column of the matrix is computed by the count of arguments of the mandatory input.

The input of the following lines

\mynPerm{9;8;7;6;5;4;3;2;1}

\mynPerm{13;15;15,65;19}

\mynPerm[,]{13;15;15,65;19}

results to:

\documentclass{article}
%\usepackage{expl3}
\usepackage{amsmath,xparse} 

\NewDocumentCommand{\myNinePerm}{ m m m m m m m m m } 
{%
    \ensuremath{%
      \begin{pmatrix}% 
        1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \\
      #1 & #2 & #3 & #4 & #5 & #6 & #7 & #8 & #9 
      \end{pmatrix}%
     }% 
}

%
\ExplSyntaxOn 
\seq_new:N \l_perm_store_seq
\seq_new:N \l_perm_tmpa_seq
\cs_generate_variant:Nn \seq_set_eq:NN {Nn}
\tl_new:N \l_perm_first_column_tl
\tl_new:N \l_perm_second_column_tl

\NewDocumentCommand \mynPerm { O{;} m } 
 {
  \seq_clear:N \l_perm_store_seq
  \seq_set_split:Nnn \l_perm_store_seq { #1 } { #2 } 
  \seq_mynPerm_set_first_column:N \l_perm_store_seq
  \seq_mynPerm_set_second_column:N \l_perm_store_seq
  \seq_mynPerm_output_matrix:NN \l_perm_first_column_tl \l_perm_second_column_tl
 }

\cs_new:Npn \seq_mynPerm_set_first_column:N #1 
 {
  \seq_set_eq:NN \l_perm_tmpa_seq #1
  \int_set_eq:NN \l_tmpa_int \c_two
  \tl_set:Nx \l_perm_first_column_tl { 1 & }
  \int_while_do:nNnn { \l_tmpa_int } < { \seq_length:N \l_perm_tmpa_seq }
   { 
    \tl_put_right:NV \l_perm_first_column_tl { \l_tmpa_int & }
    \int_incr:N \l_tmpa_int
   }
   \tl_put_right:NV \l_perm_first_column_tl { \l_tmpa_int }
%   \tl_to_str:N \l_perm_first_column_tl
 }

\cs_new:Npn \seq_mynPerm_set_second_column:N #1 
 {
   \seq_set_eq:NN \l_perm_tmpa_seq #1
   \tl_clear:N \l_perm_second_column_tl 
   \seq_map_inline:Nn \l_perm_tmpa_seq
    {
     \tl_put_right:Nn \l_perm_second_column_tl { ##1 & }
    }
   \tl_reverse:N \l_perm_second_column_tl
   \tl_remove_once:Nn \l_perm_second_column_tl { & }
   \tl_reverse:N \l_perm_second_column_tl
%   \tl_to_str:N \l_perm_second_column_tl
 }

\cs_new:Npn \seq_mynPerm_output_matrix:NN #1 #2
 {
   \group_begin:
    \tl_set_eq:NN \l_tmpa_tl #1
    \tl_set_eq:NN \l_tmpb_tl #2
    \int_set:cn {c@MaxMatrixCols} { \tl_length:N \l_tmpa_tl }
    \ensuremath{%
      \begin{pmatrix}% 
        \tl_use:N \l_tmpa_tl     \\
        \tl_use:N \l_tmpb_tl
      \end{pmatrix}%
     }%
   \group_end: 
 }
\ExplSyntaxOff 

\begin{document}
\myNinePerm{9}{8}{7}{6}{5}{4}{3}{2}{1}


\mynPerm{9;8;7;6;5;4;3;2;1}

\mynPerm{13;15;15,65;19}

\mynPerm[,]{13;15;15,65;19}
\end{document}
share|improve this answer
    
Thanks Marco, your code compiled and does what it is meant to do. Numbers of elements > 10 will produce compiler error, but I am confident this would be easily fixed. It will take me a while to understand exactly what your code is doing, and how, and during that process may get back to you with requests for explaining the odd detail. I won't be marking your answer as the one that provided the definitive answer, simply because I received another solution which somewhat shorter and seems perfect in every respect. But I certainly vote you up for your contribution. –  Reinhard Neuwirth Mar 17 '12 at 3:15
    
@ReinhardNeuwirth: I removed the limitation of 9 elements. egreg's solution is shorter because I want to handle each step in separate function ;-). However I had marked the answer of egreg too ;-) –  Marco Daniel Mar 17 '12 at 9:14
    
In a spirit of separating interface from implementation, it may be nicer to do \NewDocumentCommand\mynPerm{O{;}m}{\mynPerm_do:nn{#1}{#2}}, and define \mynPerm_do:nn separately. Also, to avoid potential conflicts in macro names, we advocate using the module name as the first piece of the macro name, so \mynPerm_seq_set_first_column:N rather than \seq_mynPerm_.... –  Bruno Le Floch Mar 17 '12 at 10:06
    
@BrunoLeFloch: Thanks for the hint. I will change it. –  Marco Daniel Mar 17 '12 at 10:54
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.