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.

I need to make a smart vector command that meets the following criteria:

  1. There are three arguments: an optional first argument specifying a delimiter (comma by default), a mandatory second argument specifying one or more comma separated components that may be numbers or mathematical expressions, an optional third argument specifying a physical unit.
  2. If the command is invoked with \smartvector{2,4,3}[m] then it should typeset the numbers as a delimited list in angle brackets, separated by the delimiter specified in requirement 1, with the unit outside the right bracket. All the entries in the comma separated list must be numbers. See the MWE below; I already have this working. Note that the unit is optional and may be omitted. Here's an example.

    Example 1

  3. I'd like to add the capability to invoked the command with \smartvector{2}[m] and have it just typeset the number and the unit with no brackets or commas. So I think I need to test whether the comma separated list in the mandatory second argument is a single number. Here's an example.

    Example 2

  4. If the comma separated list contains ANY item that is NOT a number, as in \smartvector{2x^2,y^3,2} then it should typeset as in requirement 2 but MUST leave off the unit. The user will know to leave off the unit as algebraic expressions don't have units. Here's an example.

    Example 3

So I think it comes down to parsing the comma separated list and doing some logic, but I must confess to not understanding that part of my MWE, which was offered to me here in another thread. I don't want to be guilty of cargo cult programming any more than I already am.

Here's my MWE:

\documentclass[10pt]{article}
\usepackage{xargs}

\makeatletter
\newcommandx\smartvector[3][1={,},3=,usedefault]{\ensuremath{%
  \global\def\smart@delim{#1}%
    \left\langle
      \smart@vector #2,\relax\noexpand\@eolst%
    \right\rangle{\;#3}}}%
\def\smart@vector #1,#2\@eolst{%
  \ifx\relax#2\relax
    #1
  \else
    #1\smart@delim
    \smart@vector #2\@eolst%
  \fi}
\makeatother

\newcommand{\m}{\ensuremath{\mathrm{m}}}

\begin{document}

\[
\smartvector{2,4,3}[\m]
\]

\end{document}
share|improve this question
    
Just to be clear: you want the input to always be comma separated, but the user should be able to specify a different delimiter that will appear in the output? I've edited my answer to reflect that. –  Scott H. Sep 27 '12 at 19:35
    
@scott Yes that's correct. –  user19024 Sep 27 '12 at 20:51
add comment

2 Answers

up vote 9 down vote accepted

If I understand correctly, the following should work:

  • It seems to me that your cases (1) and (3) are identical if the user will know to leave the units out. The following makes that assumption.

enter image description here


\documentclass{article}
\usepackage{xparse}
\usepackage{mathtools}

\ExplSyntaxOn
\NewDocumentCommand{\smartvector}{ O{,} m O{} }
    {
      \smart_vector:nnn {#1}{#2}{#3}
    }

\cs_new:Npn \smart_vector:nnn #1#2#3
{
\tl_set:Nn \l_tmpa_tl {#2}
\tl_replace_all:Nnn \l_tmpa_tl {,}{#1}
\tl_if_blank:nTF {#3}
  {
    \langle \tl_use:N \l_tmpa_tl \rangle
  }
  {
    \tl_if_in:NnTF \l_tmpa_tl {#1}
        {
            \langle\tl_use:N \l_tmpa_tl\rangle\ \mathrm{#3}
        }
        {
            \tl_use:N \l_tmpa_tl\ \mathrm{#3}
        }
    }
}

\ExplSyntaxOff
\begin{document}
$\smartvector[;]{1,2,3}[m/s]$\par
$\smartvector{1,2}$\par
$\smartvector{1}[m]$\par
$\smartvector{x^2,y^2}$\par
\end{document}
share|improve this answer
    
Excellent! What a great way to learn LaTeX3 too. Is is safe to put LaTeX3 code into a style coded otherwise in LaTeX2e? –  user19024 Sep 27 '12 at 20:52
    
Yes, as long as you load the xparse package everything will be fine :) –  Scott H. Sep 27 '12 at 20:56
add comment

A shorter LaTeX3 version that uses a recent addition (\seq_use:Nnnn); I also use siunitx for the units.

\documentclass{article}
\usepackage{siunitx,xparse}

\ExplSyntaxOn
\NewDocumentCommand{\smartvector}{ O{,} m o }
 {
  \joe_smart_vector:nn { #1 } { #2 }
  \IfValueT{#3}{\,\si{#3}}
 }

\seq_new:N \l__joe_list_seq
\cs_new_protected:Npn \joe_smart_vector:nn #1 #2
 {
  \seq_set_split:Nnn \l__joe_list_seq { , } { #2 }
  \int_compare:nF { \seq_count:N \l__joe_list_seq = 1 } { \langle }
  \seq_use:Nnnn \l__joe_list_seq { #1 } { #1 } { #1 }
  \int_compare:nF { \seq_count:N \l__joe_list_seq = 1 } { \rangle }
 }
\ExplSyntaxOff

\begin{document}
$\smartvector[;]{1,2,3}[m/s]$\par
$\smartvector{1,2}$\par
$\smartvector{1}[m]$\par
$\smartvector{x^2,y^2}$\par
\end{document}
share|improve this answer
    
Can you comment on how your solution differs from Scotts? I'm very new to LaTeX3 and don't understand the internals yet. –  user19024 Oct 3 '12 at 14:33
    
@Joe The difference is that I don't do any substitution, but rather split the argument into its components. If there is only one component (the sequence's length returned by \seq_count:N \l__joe_list_seq will be 1) I add the angle brackets; the items in the sequence will be delivered separated by the chosen separator (the optional argument to \smartvector). –  egreg Oct 3 '12 at 14:49
add comment

Your Answer

 
discard

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