LaTeX3 - 再利用されるデータ構造

LaTeX3 - 再利用されるデータ構造

次のようなタイプの複数のデータを保存して、書式設定と計算の両方で操作できるようにしたいと考えています。ユーザーのコードで分析される行数は事前に固定されていません。

この値を抽出するパーサーはすでに作成しました。

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

この単純な状況では、次の出力を生成します。nicematrix表を参照してください。ただし、これは最初の機能にすぎません。たとえば、ドキュメントの後半で最大値を計算したいとします。そのためには、ラベルとそれに関連付けられた値を見つけるなど、fコンテキストを回復する必要があります。imgsf(t)

\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}

2つの可能性があると思います。

  1. どちらのパッケージを使用するかはあなた次第ですstarrayが、このプロジェクトは長期的には維持されないのではないかと心配しています。

  2. または、プロパティ リストのリストのように見える自家製ソリューションを実装することもできます。

JSONのような構造が期待される [2024-03-09.v1]

アイデアとしては、各テーブルに変数を関連付け、辞書ごとに反復処理できる変数を用意し、取得した辞書ごとに異なるタイプの値にアクセスできるようにすることです。

[
    {
        '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'
    }
]

追加情報 [2024-01-17.v1]

  1. パーサーはl3-token リストを提供します。

  2. キーの値は、VALコンマ区切りのリストとして、または関数によるシーケンスとして使用できますL3。 2 番目の選択肢の方が適しているはずです。

  3. 値はさまざまな状況で使用され、時には表を印刷したり、時には計算を実行したりします。これは、この答え

概念実証の簡略化 [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}

答え1

アップデート

コードは、環境savefunctableとコマンドに分割されています\usefunctable。どちらも最初の引数として名前を取ります。環境の内容は内部に保存され、適切な名前を指定してsavefunctable後でコマンドで使用できます。\usefunctable

ここに画像の説明を入力してください

\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}

元の回答

環境functableが定義されます。必須の引数を 1 つ取り、その後に本体を取ります。

最初に本体が;で分割されます\seq_set_split:Nnn。次に項目が で分割されます。や=などのキーは に格納されます。xvalsimgs\l__projetmbc_key_tl

t同様に、や などの名前f(t)は に格納されます\l__projetmbc_name_tl

環境はfunctable最初の必須引数によって異なります。以下はNiceArray、、、maximum of xvalsおよびlist of imgsの例ですplot

ここに画像の説明を入力してください

\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}

関連情報