LaTeX3: estructura de datos para reutilizar

LaTeX3: estructura de datos para reutilizar

Quiero almacenar varios datos, cada uno del siguiente tipo, para poder manipularlos tanto para formatear como para realizar cálculos. El número de líneas analizadas en el código del usuario no está fijado de antemano.

Ya hice el analizador que extrae estos valores.

LINE: 1
    CTXT: xvals
    LABEL: t
    VAL: 1, 20, 300, 4000

LINE: 2
    CTXT: imgs
    LABEL: f(t)
    VAL: a , b, bb, ccc, dddd

LINE: 3
    CTXT: imgs
    LABEL: g(t)
    VAL: U, V, VV, WWW, XXXX

En esta situación simple, generaré el siguiente resultado (consulte la nicematrixtabla), pero esta es solo la primera funcionalidad. Me gustaría, por ejemplo, calcular el máximo fmás adelante en el documento. Para hacer eso, necesito recuperar el contexto imgspara encontrar la etiqueta f(t)y sus valores asociados.

\documentclass[11pt, a4paper]{article}

\usepackage{nicematrix}

\begin{document}

\section{What the user types}

\begin{verbatim}
\begin{functable}
    xvals =    t : 1 , 20 , 300 , 4000 ;
    imgs  = f(t) : a , bb , ccc , dddd ;
            g(t) : U , VV , WWW , XXXX
\end{functable}
\end{verbatim}


\section{What the user sees}

$\begin{NiceArray}[cell-space-limits = 3pt]{c*{4}{|c}}
    t    & 1 & 20 & 300 & 4000
\\ \hline
    f(t) & a & bb & ccc & dddd
\\ \hline
    g(t) & U & VV & VVV & WWW
\end{NiceArray}$


\section{What can be do after}

The user can ask for example to rebuild the 1st table 
with something like this.

\begin{verbatim}
\begin{functable}[rebuild=1]
\end{functable}
\end{verbatim}

The user can also ask to calculate the maximum value 
of $f(t)$ via something like \verb+\calcfunctable{max=f(t)}+.

\end{document}

Veo dos posibilidades.

  1. O utilice el starraypaquete, pero me temo que este proyecto no se mantendrá a largo plazo.

  2. O puedo intentar implementar una solución casera que parezca una lista de listas de propiedades.

Se espera una estructura similar a JSON [2024-03-09.v1]

La idea es tener una variable asociada a cada tabla, una variable que se pueda iterar diccionario a diccionario, y por cada diccionario así obtenido tendremos acceso a los diferentes tipos de valores.

[
    {
        'CTX'  : 'xvals',
        'VAL'  : ['1', '20', '300', '4000'],
        'LABEL': 't',
        'LINE' : '1'
    },
    {
        'CTX'  : 'imgs',
        'VAL'  : ['a' , 'b', 'bb', 'ccc', 'dddd'],
        'LABEL': 'f(t)',
        'LINE' : '2'
    },
    {
        'CTX'  : 'imgs',
        'VAL'  : ['U', 'V', 'VV', 'WWW', 'XXXX'],
        'LABEL': 'g(t)',
        'LINE' : '3'
    }
]

Alguna información adicional [2024-01-17.v1]

  1. El analizador proporciona l3listas de tokens.

  2. El valor de VALlas claves se puede utilizar como listas separadas por comas o como secuencias por L3funciones. La segunda opción debería ser mejor.

  3. Los valores se utilizarán en diferentes situaciones, a veces para imprimir una tabla, a veces para realizar cálculos. Esto debería ser similar a lo que se hace enesta respuesta.

Prueba de concepto simplificada [2024-03-09.v1]

\documentclass[12pt]{article}

\ExplSyntaxOn

% -- FUNCTABLE ENV. -- %

\NewDocumentEnvironment{ functable }{ b }{%
  \tns_functab_functable:n { #1 }
}{}


\cs_new:Npn \tns_functab_functable:n #1 {
  \tns_core_DSL_ctxt_parser:nn { functable } { #1 } 

  \bigskip\par
  NEW ~ DATA ~ \int_use:N \g_tns_functab_id_int

  \par
  TODO
}


% -- DSL - L3 -- %

\tl_new:N   \g_tns_functab_semicolon_tl
\tl_gset:Nn \g_tns_functab_semicolon_tl { ; }

\tl_new:N   \g_tns_functab_colon_tl
\tl_gset:Nn \g_tns_functab_colon_tl { : }

\cs_generate_variant:Nn \seq_set_split:Nnn { NV }

\AtBeginDocument {
  \tl_gset_rescan:NnV \g_tns_functab_semicolon_tl
                      {}
                      \g_tns_functab_semicolon_tl

  \tl_gset_rescan:NnV \g_tns_functab_colon_tl
                      {}
                      \g_tns_functab_colon_tl
}


% :: AGNOSTIC PARSERS :: %

\int_new:N   \g_tns_functab_id_int
\int_gset:Nn \g_tns_functab_id_int { 0 }

\int_new:N \l_tns_core_ctxt_nb_line_int
\seq_new:N \l_tns_core_ctxts_seq
\tl_new:N  \l_tns_core_current_ctxt_tl


\cs_new:Npn \tns_core_DSL_ctxt_parser:nn #1#2 {
% The ID nb. of the env.
  \int_gincr:N \g_tns_functab_id_int

% Line by line parsing.
%
% Lines are semi-colon separated.
  \seq_set_split:NVn \l_tns_core_ctxts_seq
                      \g_tns_functab_semicolon_tl
                      { #2 }

  \int_zero:N \l_tns_core_ctxt_nb_line_int

  \seq_map_inline:Nn \l_tns_core_ctxts_seq {
    \bigskip\par

    \int_incr:N \l_tns_core_ctxt_nb_line_int

    Line ~ \int_use:N \l_tns_core_ctxt_nb_line_int
    \par

% Get the context and its content.
    \seq_set_split:Nnn \l_tmpa_seq { = } { ##1 }

    \int_set:Nn \l_tmpa_int {\seq_count:N \l_tmpa_seq}

    \quad
    \int_case:nnF { \int_use:N \l_tmpa_int } {
      { 1 } {
        LAST - CTXT: ~
        (\tl_use:N \l_tns_core_current_ctxt_tl)
      }
      { 2 } {
        \seq_pop_left:NN \l_tmpa_seq \l_tns_core_current_ctxt_tl

        NEW - CTXT: ~
        (\tl_use:N \l_tns_core_current_ctxt_tl)
      }
    }{
      ILLEGAL!
    }

% Get the optional label and its content.
    \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl

    \seq_set_split:NVV \l_tmpa_seq
                       \g_tns_functab_colon_tl
                       \l_tmpa_tl

    \int_set:Nn \l_tmpa_int {\seq_count:N \l_tmpa_seq}

    \par\quad
    \int_case:nnF { \int_use:N \l_tmpa_int } {
      { 1 } {
        NO LABEL
      }
      { 2 } {
        \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl

        LABEL: ~
        $(\tl_use:N \l_tmpa_tl)$
      }
    }{
      ILLEGAL!
    }

    \par\quad

    \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl

    VAL: ~
    $(\tl_use:N \l_tmpa_tl)$
  }
}

\ExplSyntaxOff


\begin{document}

\begin{functable}
    xvals =    t : 1 , 20 , 300 , 4000 ;
    imgs  = f(t) : a , bb , ccc , dddd ;
            g(t) : U , VV , WWW , XXXX
\end{functable}

\end{document}

Respuesta1

Actualizar

El código se divide en un entorno savefunctabley un comando \usefunctable. Ambos toman un nombre como primer argumento. El contenido del entorno savefunctablese almacena internamente y se puede utilizar más adelante con el comando \usefunctableespecificando el nombre apropiado.

ingrese la descripción de la imagen aquí

\documentclass[border=6pt,varwidth]{standalone}
\usepackage{nicematrix}
\usepackage{pgfplots}
\pgfplotsset{compat=1.18}
\ExplSyntaxOn
\cs_generate_variant:Nn \seq_map_indexed_inline:Nn { cn }
\cs_generate_variant:Nn \seq_gset_from_clist:Nn { ce }
\seq_new:N \l__projetmbc_functable_seq
\seq_new:N \l__projetmbc_key_name_list_seq
\tl_new:N \l__projetmbc_coordinates_tl
\tl_new:N \l__projetmbc_key_tl
\tl_new:N \l__projetmbc_name_tl
\NewDocumentEnvironment { savefunctable } { m +b }
  {
    \seq_set_split:Nnn \l__projetmbc_functable_seq { ; } {#2}
    \seq_map_inline:Nn \l__projetmbc_functable_seq
      {
        \seq_set_split:Nnn \l__projetmbc_key_name_list_seq { = } {##1}
        \int_compare:nNnT { \seq_count:N \l__projetmbc_key_name_list_seq } = { 2 }
          {
            \seq_pop_left:NN \l__projetmbc_key_name_list_seq \l__projetmbc_key_tl
            \seq_gclear_new:c { g__projetmbc_#1_key_\l__projetmbc_key_tl _seq }
          }
        \seq_set_split:Nee \l__projetmbc_key_name_list_seq { \c_colon_str } { \seq_item:Nn \l__projetmbc_key_name_list_seq { 1 } }
        \seq_pop_left:NN \l__projetmbc_key_name_list_seq \l__projetmbc_name_tl
        \seq_gput_right:cV { g__projetmbc_#1_key_\l__projetmbc_key_tl _seq } \l__projetmbc_name_tl
        \seq_gclear_new:c { g__projetmbc_#1_key_\l__projetmbc_key_tl _name_\l__projetmbc_name_tl _seq }
        \seq_gset_from_clist:ce { g__projetmbc_#1_key_\l__projetmbc_key_tl _name_\l__projetmbc_name_tl _seq } { \seq_item:Nn \l__projetmbc_key_name_list_seq { 1 } }
      }
  }
  {}
\NewDocumentCommand \usefunctable { m m }
  {
    \str_case:nnF {#2}
      {
        { list~of~imgs }
          {
            \seq_use:cn { g__projetmbc_#1_key_imgs_seq } { , ~ }
          }
        { maximum~of~xvals }
          {
            \fp_eval:n { max ( \seq_use:cn { g__projetmbc_#1_key_xvals_name_\seq_item:cn { g__projetmbc_#1_key_xvals_seq } { 1 }_seq } { , } ) }
          }
        { NiceArray }
          {
            $
              \begin { NiceArray }
                [ cell-space-limits = 3pt ]
                { c * { \seq_count:c { g__projetmbc_#1_key_xvals_name_\seq_item:cn { g__projetmbc_#1_key_xvals_seq } { 1 }_seq } } { | c } }
                \seq_item:cn { g__projetmbc_#1_key_xvals_seq } { 1 } & \seq_use:cn { g__projetmbc_#1_key_xvals_name_\seq_item:cn { g__projetmbc_#1_key_xvals_seq } { 1 }_seq } { & } \\ \hline
                \seq_map_indexed_inline:cn { g__projetmbc_#1_key_imgs_seq }
                  {
                    ##2 & \seq_use:cn { g__projetmbc_#1_key_imgs_name_##2_seq } { & }
                    \int_compare:nNnF {##1} = { \seq_count:c { g__projetmbc_#1_key_imgs_seq } }
                      { \\ \hline }
                  }
              \end { NiceArray }
            $
          }
        { plot }
          {
            \tl_build_begin:N \l__projetmbc_coordinates_tl
            \seq_map_indexed_inline:cn { g__projetmbc_#1_key_xvals_name_\seq_item:cn { g__projetmbc_#1_key_xvals_seq } { 1 }_seq }
              {
                \tl_build_put_right:Ne \l__projetmbc_coordinates_tl
                  { ( ##2 , \seq_item:cn { g__projetmbc_#1_key_imgs_name_\seq_item:cn { g__projetmbc_#1_key_imgs_seq } { 1 }_seq } {##1} ) }
              }
            \tl_build_end:N \l__projetmbc_coordinates_tl
            \begin { tikzpicture }
              \begin { axis }
                [
                  ymin = 0
                ]
                \addplot coordinates
                  { \l__projetmbc_coordinates_tl }
                ;
              \end { axis }
            \end { tikzpicture }
          }
      }
      { \errmessage { wrong~option~for~functable~environment } }
  }
\ExplSyntaxOff
\begin{document}
\begin{savefunctable}{example 1}
    xvals =    t : 1 , 20 , 300 , 4000 ;
    imgs  = f(t) : a , bb , ccc , dddd ;
            g(t) : U , VV , WWW , XXXX
\end{savefunctable}
\begin{savefunctable}{example 2}
    xvals =    t : 1 , 2 , 3 , 4 ;
    imgs  = f(t) : 8 , 5 , 7 , 6 ;
\end{savefunctable}

Using \texttt{example 1} with \texttt{NiceArray}: \usefunctable{example 1}{NiceArray}

The maximum of the \texttt{xvals} of \texttt{example 2}: \usefunctable{example 2}{maximum of xvals}

The list of \texttt{imgs} of \texttt{example 1}: \usefunctable{example 1}{list of imgs}

A \texttt{plot} for \texttt{example 2}: \usefunctable{example 2}{plot}
\end{document}

Respuesta original

functableSe define un entorno . Se necesita un argumento obligatorio y luego el cuerpo.

Primero se divide el cuerpo ;con \seq_set_split:Nnn. Luego los artículos se dividen =. Una clave como xvalso imgsse almacena en \l__projetmbc_key_tl.

De manera similar, un nombre como to f(t)se almacena en \l__projetmbc_name_tl.

El medio ambiente functabledepende del primer argumento obligatorio. A continuación se muestran ejemplos de NiceArray, maximum of xvalsy .list of imgsplot

ingrese la descripción de la imagen aquí

\documentclass[border=6pt,varwidth]{standalone}
\usepackage{nicematrix}
\usepackage{pgfplots}
\pgfplotsset{compat=1.18}
\ExplSyntaxOn
\cs_generate_variant:Nn \seq_map_indexed_inline:Nn { cn }
\cs_generate_variant:Nn \seq_set_from_clist:Nn { ce }
\seq_new:N \l__projetmbc_functable_seq
\seq_new:N \l__projetmbc_key_name_list_seq
\tl_new:N \l__projetmbc_coordinates_tl
\tl_new:N \l__projetmbc_key_tl
\tl_new:N \l__projetmbc_name_tl
\NewDocumentEnvironment { functable } { m +b }
  {
    \seq_set_split:Nnn \l__projetmbc_functable_seq { ; } {#2}
    \seq_map_inline:Nn \l__projetmbc_functable_seq
      {
        \seq_set_split:Nnn \l__projetmbc_key_name_list_seq { = } {##1}
        \int_compare:nNnT { \seq_count:N \l__projetmbc_key_name_list_seq } = { 2 }
          {
            \seq_pop_left:NN \l__projetmbc_key_name_list_seq \l__projetmbc_key_tl
            \seq_clear_new:c { l__projetmbc_key_\l__projetmbc_key_tl _seq }
          }
        \seq_set_split:Nee \l__projetmbc_key_name_list_seq { \c_colon_str } { \seq_item:Nn \l__projetmbc_key_name_list_seq { 1 } }
        \seq_pop_left:NN \l__projetmbc_key_name_list_seq \l__projetmbc_name_tl
        \seq_put_right:cV { l__projetmbc_key_\l__projetmbc_key_tl _seq } \l__projetmbc_name_tl
        \seq_clear_new:c { l__projetmbc_key_\l__projetmbc_key_tl _name_\l__projetmbc_name_tl _seq }
        \seq_set_from_clist:ce { l__projetmbc_key_\l__projetmbc_key_tl _name_\l__projetmbc_name_tl _seq } { \seq_item:Nn \l__projetmbc_key_name_list_seq { 1 } }
      }
    \str_case:nnF {#1}
      {
        { list~of~imgs }
          {
            \seq_use:Nn \l__projetmbc_key_imgs_seq { , ~ }
          }
        { maximum~of~xvals }
          {
            \fp_eval:n { max ( \seq_use:cn { l__projetmbc_key_xvals_name_\seq_item:Nn \l__projetmbc_key_xvals_seq { 1 }_seq } { , } ) }
          }
        { NiceArray }
          {
            $
              \begin { NiceArray }
                [ cell-space-limits = 3pt ]
                { c * { \seq_count:c { l__projetmbc_key_xvals_name_\seq_item:Nn \l__projetmbc_key_xvals_seq { 1 }_seq } } { | c } }
                \seq_item:Nn \l__projetmbc_key_xvals_seq { 1 } & \seq_use:cn { l__projetmbc_key_xvals_name_\seq_item:Nn \l__projetmbc_key_xvals_seq { 1 }_seq } { & } \\ \hline
                \seq_map_indexed_inline:Nn \l__projetmbc_key_imgs_seq
                  {
                    ##2 & \seq_use:cn { l__projetmbc_key_imgs_name_##2_seq } { & }
                    \int_compare:nNnF {##1} = { \seq_count:N \l__projetmbc_key_imgs_seq }
                      { \\ \hline }
                  }
              \end { NiceArray }
            $
          }
        { plot }
          {
            \tl_build_begin:N \l__projetmbc_coordinates_tl
            \seq_map_indexed_inline:cn { l__projetmbc_key_xvals_name_\seq_item:Nn \l__projetmbc_key_xvals_seq { 1 }_seq }
              {
                \tl_build_put_right:Ne \l__projetmbc_coordinates_tl
                  { ( ##2 , \seq_item:cn { l__projetmbc_key_imgs_name_\seq_item:Nn \l__projetmbc_key_imgs_seq { 1 }_seq } {##1} ) }
              }
            \tl_build_end:N \l__projetmbc_coordinates_tl
            \begin { tikzpicture }
              \begin { axis }
                [
                  ymin = 0
                ]
                \addplot coordinates
                  { \l__projetmbc_coordinates_tl }
                ;
              \end { axis }
            \end { tikzpicture }
          }
      }
      { \errmessage { wrong~option~for~functable~environment } }
  }
  {}
\ExplSyntaxOff
\begin{document}
Using \texttt{NiceArray}:

\begin{functable}{NiceArray}
    xvals =    t : 1 , 20 , 300 , 4000 ;
    imgs  = f(t) : a , bb , ccc , dddd ;
            g(t) : U , VV , WWW , XXXX
\end{functable}

The maximum of the \texttt{xvals}:
\begin{functable}{maximum of xvals}
    xvals =    t : 1 , 20 , 300 , 4000 ;
    imgs  = f(t) : a , bb , ccc , dddd ;
            g(t) : U , VV , WWW , XXXX
\end{functable}

The list of \texttt{imgs}:
\begin{functable}{list of imgs}
    xvals =    t : 1 , 20 , 300 , 4000 ;
    imgs  = f(t) : a , bb , ccc , dddd ;
            g(t) : U , VV , WWW , XXXX
\end{functable}

A plot:
\begin{functable}{plot}
    xvals =    t : 1 , 2 , 3 , 4 ;
    imgs  = f(t) : 8 , 5 , 7 , 6 ;
\end{functable}
\end{document}

información relacionada