1

I need to pass a vector, defined as a clist, to a function that parses it into components for multiple subsequent calculations. The parser seems to work perfectly if the vector clist is passed directly to it as a parameter, but not when it's defined as a command like say, \vectora.

In the long run I want to do vector computations in LaTeX3 but first, I need a way of defining vectors as commands with the components expressed as a clist. I envision something like \DefineVector[b]{1,2,3} where [b] is an optional label with default value a and {1,2,3} is a clist containing the numerical components of the vector. The label could be as simple as one letter, or it could be something like vectorb. The resulting vector, e.g. \vectora would then expand to, e.g. {1,2,3} when passed to a parser. The parser would be a programming layer function that the user doesn't actually interact with, but would be used in other functions that do actual vector computations like magnitude, dot product, or cross product. The parser would take the vector components, e.g. {1,2,3} and one by one assign them to variables like vectorax and vectoray and vectoraz which would then subsequently be used in floating point calculations. In this example, \vectorax would expand to 1, \vectoray would expand to 2, and \vectoraz would expand to 3. So if I were to define a new function for calculating, say, a cross product, I'd parse two vectors, vectora and vectorb, and end up with two sets of components with which I could calculate the cross product. The problem I'm having is creating a vector that correctly passes to the parser.

In the code below, which is the result of having tinkered with this for at least two or three hours, the \ParseVector command works when I pass a clist with a vector's components, but doesn't work when I pass what I thought was a clist defined as part of another command. That is the immediate problem I need to solve, and I'm obviously missing some important concepts. I'm doing this while also trying to conform to LaTeX3 coding standards.

\documentclass[10pt]{article}
\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn
\cs_new_protected:Nn \joe_parsevector:n {%
  % Create sets temporary clist to #1
  \clist_set:Nn \l_tmpa_clist { #1 }
  % Applies { ... } to each element in the temporary clist.
  %\clist_map_inline:Nn \l_tmpa_clist { [##1] }
  \begin{enumerate}
    \clist_map_inline:Nn \l_tmpa_clist { \item ##1 }
  \end{enumerate}
}%
\NewDocumentCommand{\ParseVector}{ m }{%
  \joe_parsevector:n { #1 }
}%

\NewDocumentCommand{\DefineVector}{ m m }{%
  [#1][#2]
  %\exp_args:Nc \newcommand{#1}[1]{#2}
  \cs_new:cpn {#1} ##1 { #2 }
}%
\ExplSyntaxOff

\begin{document}
Hello.

Parsing the vector \verb!{5,-3,4}! gives \ParseVector{5,-3,4}

\DefineVector{vectora}{3,-5,7}

\vectora

\end{document}
4
  • In your example, \vectora is defined as a one-argument macro. How do you expect to use \vectora? – muzimuzhi Z Jul 1 '20 at 6:49
  • Judging from the text, it seems you want to define \vectora, \vectorax, \vectoray and \vectoraz. Is that so? – egreg Jul 1 '20 at 10:37
  • 1
    It wouldn't be difficult to define \vectora to accept a numeric argument for the component, with 0 denoting the full vector as a clist. – egreg Jul 1 '20 at 10:42
  • I have edited the question to, hopefully, address your questions and to clarify my objectives. I'm also open to better ways of doing this. I teach LaTeX in my physics courses and my document level code will eventually make it into a package I give to my students. – LaTeXereXeTaL Jul 1 '20 at 20:05
2

If I understand correctly, you want to store vectors with a symbolic name and use them to perform operations.

I'll show how to define the storing function (in sequences, rather than clists) and some operations: magnitude, scalar product, vector (cross) product.

The symbolic name can be any sequence of letters and digits.

The first two are expandable, the last one isn't, because we need to store the result.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\DefineVector}{O{a}m}
 {
  \joe_vector_define:nn { #1 } { #2 }
 }
\NewExpandableDocumentCommand{\PrintVector}{m}
 {
  (\seq_use:cn { l_joe_vector_#1_seq } { , })
 }
\NewExpandableDocumentCommand{\VectorMagnitude}{O{15}m}
 {
  \joe_vector_magnitude:nn { #1 } { #2 }
 }
\NewExpandableDocumentCommand{\ScalarProduct}{O{15}mm}
 {
  \joe_vector_scalarproduct:nnn { #1 } { #2 } { #3 }
 }
\NewDocumentCommand{\VectorProduct}{mmm}
 {% #1 = first vector, #2 = second vector, #3 = result
  \joe_vector_vectorproduct:nnn { #1 } { #2 } { #3 }
 }

\cs_new_protected:Nn \joe_vector_define:nn
 {
  \seq_clear_new:c { l_joe_vector_#1_seq }
  \seq_set_from_clist:cn { l_joe_vector_#1_seq } { #2 }
 }

\cs_new:Nn \joe_vector_magnitude:nn
 {
  \fp_eval:n
   {
    round
     (
      sqrt( \seq_map_function:cN { l_joe_vector_#2_seq } \__joe_vector_square:n )
      ,
      #1
     )
   }
 }
\cs_new:Nn \__joe_vector_square:n { + (#1)^2 }

\cs_new:Nn \joe_vector_scalarproduct:nnn
 {
  \fp_eval:n
   {
    round
     (
      \seq_mapthread_function:ccN { l_joe_vector_#2_seq } { l_joe_vector_#3_seq } \__joe_vector_product:nn
      ,
      #1
     )
   }
 }
\cs_new:Nn \__joe_vector_product:nn
 {
  +(#1)*(#2)
 }

\cs_new_protected:Nn \joe_vector_vectorproduct:nnn
 {
  \seq_clear_new:c { l_joe_vector_#3_seq }
  \seq_put_right:cx { l_joe_vector_#3_seq }
   {
    \fp_eval:n
     {
      (\seq_item:cn { l_joe_vector_#1_seq } { 2 }) * (\seq_item:cn { l_joe_vector_#2_seq } { 3 })
      -
      (\seq_item:cn { l_joe_vector_#1_seq } { 3 }) * (\seq_item:cn { l_joe_vector_#2_seq } { 2 })
     }
   }
  \seq_put_right:cx { l_joe_vector_#3_seq }
   {
    \fp_eval:n
     {
      (\seq_item:cn { l_joe_vector_#1_seq } { 3 }) * (\seq_item:cn { l_joe_vector_#2_seq } { 1 })
      -
      (\seq_item:cn { l_joe_vector_#1_seq } { 1 }) * (\seq_item:cn { l_joe_vector_#2_seq } { 3 })
     }
   }
  \seq_put_right:cx { l_joe_vector_#3_seq }
   {
    \fp_eval:n
     {
      (\seq_item:cn { l_joe_vector_#1_seq } { 1 }) * (\seq_item:cn { l_joe_vector_#2_seq } { 2 })
      -
      (\seq_item:cn { l_joe_vector_#1_seq } { 2 }) * (\seq_item:cn { l_joe_vector_#2_seq } { 1 })
     }
   }
 }
\ExplSyntaxOff

\begin{document}

\DefineVector[a]{0,-4,0}
\DefineVector[b]{1,1,1}
\DefineVector[x4]{1,2,3,4}
\DefineVector[y4]{-1,1,0,2}

$\VectorMagnitude{a}$
$\VectorMagnitude{b}$
$\VectorMagnitude[2]{b}$

$\ScalarProduct{a}{b}$

$\ScalarProduct{x4}{y4}$

\VectorProduct{a}{b}{c}
$\PrintVector{c}$

\end{document}

enter image description here

The cross product is implemented the hard way, because it's not really an operation on vectors.

1
  • I'm very grateful. I hadn't really intended for you to write all the code, but this is the kind of concrete example I'm not otherwise finding. I will study it in detail. I plan to extend this kind of functionality to include matrices and multiplying matrices and vectors (i.e. contracting a vector with the metric tensor, etc.). – LaTeXereXeTaL Jul 2 '20 at 3:02

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.