Expl3 para simplificar problemas de expansão com conteúdo Tabularray construído em loop

Expl3 para simplificar problemas de expansão com conteúdo Tabularray construído em loop

Uma continuação deminha pergunta anteriorrespondido graças a @egreg. (Talvez o Prof. Gregorio possa me ajudar aqui também.)

Situação

Estou criando uma macro Tabela de Perguntas (ToQ) para criar uma tabela de notas na capa de um exame ou tarefa. Estava funcionando bem, tabularmas mudei para o Tabularray tblr, pelo qual estou me apaixonando. Infelizmente, como costumo totcountcriar contadores para as notas em cada nova questão à medida que elas são encontradas, me deparo com questões de expansão que hoje me deixam perplexo.

Problema

Loops não podem ser colocados dentro de um tblrambiente (veja§3.2.3do manual), como é o caso dos tabulares comuns. Mas as soluções que parecem funcionar para tabelas comuns:

  • usando loops primitivos LaTeX,
  • construindo o corpo fora da mesa como um \gdef feito de \edefs, e
  • construindo o corpo como um registro de token,

não trabalhei para tblr. Além disso, tblrpermite especificar uma expandopção para especificar tokens para expansão antes de processar o corpo. (Mostrarei isso no MWE posterior.)

O que eu gostaria

Inspirado na resposta de @frougonaqui, eu gostaria de adotar uma abordagem completamente expl3, já que estou tentando aprender expl3 de qualquer maneira. Mas estou perdendo a cabeça e gostaria de receber ajuda. Não apenas uma solução, se você puder ser gentil, gostaria de um breve esboço para que possa estudar a solução rastreando os documentos.

MWE

\documentclass{article}
\usepackage{tabularray}
\usepackage{xcolor}

\usepackage{totcount}

% Stuff for keeping account of questions and their scores
% ........................................................<<<
\newcounter{questioncount}
\setcounter{questioncount}{0}
\regtotcounter{questioncount}%
\newcounter{scoretotal}
\setcounter{scoretotal}{0}
\regtotcounter{scoretotal}%

\newcommand{\setquestionpoints}[2]{%
  \expanded{\noexpand\newtotcounter{qpoints#1}}%
  \expanded{\noexpand\setcounter{qpoints#1}}{#2}%
}
\newcommand{\getquestionpoints}[1]{%
  \ifnum\value{qpoints#1}>0
    \arabic{qpoints#1}%
  \else
    0%
  \fi
}

\newcommand{\TOTAL}[1]{%
  \ifcsname c@#1@totc\endcsname
    \total{#1}%
  \else
    ??%
  \fi
}
% ........................................................>>>

% Typesetting questions
% ........................................................<<<
\newcommand{\nquestion}[1]{%
  \stepcounter{questioncount}%
  \setquestionpoints{\arabic{questioncount}}{#1}%
  \addtocounter{scoretotal}{#1}%
  Question~\thequestioncount (#1 marks.)%
}
% ........................................................>>>

\ExplSyntaxOn

\cs_new_protected:Npn \my_typeset_table_generic:nn #1#2
  {
    \group_begin:
      \cs_set_protected:Npn \__my_typeset_table_generic_tmp_func:n ##1 {#1}
      \__my_typeset_table_generic_tmp_func:n {#2}
    \group_end:
  }

\cs_generate_variant:Nn \my_typeset_table_generic:nn { nV }

\ExplSyntaxOff

\begin{document}

\ExplSyntaxOn

% This works, but I would like to know how expl3 can help me build this
%  list automatically, looping for
%      q ∈ [1, \totvalue{questioncount}]
%  to obtain \TOTAL{qpoints\theq} in each cell of the middle column.
\tl_set:Nn \l_my_tabular_tl {%
    1 & \TOTAL{qpoints1} & \null \\ \hline
    2 & \TOTAL{qpoints2} & \null \\ \hline
    3 & \TOTAL{qpoints3} & \null \\ \hline
}
\my_typeset_table_generic:nV
  {
    \SetTblrInner{rowsep=1ex}%
    \begin{tblr}{colspec={|ccX[c]|}, width=0.35\linewidth}
      \SetRow{black, rowsep=0.25ex}
      \color{white}\textit{Q}\textnumero & \color{white}Marks & \color{white}Grade \\ \hline
      #1
    \SetCell[c=2]{c}~TOTAL:~&~\null\\\cline[r]{3-3}
           && {\TOTAL{scoretotal}}\\\hline
    \end{tblr}
  }
  \l_my_tabular_tl
\ExplSyntaxOff

%%% Make some questions:
\vspace{2cm}
\noindent
\nquestion{10}\\
\nquestion{12}\\
\nquestion{15}\\


\end{document}

Produz:

Responder1

O último \\depois \nquestion{15}causou um Badbox e foi removido.

O tblrambiente pode ser colocado fora do código expl3. A opção expandno tblrambiente é usada. Então \my_typeset_table_generic:nnnão é mais necessário.

No tblrambiente, a opção rowsep=1exé colocada na lista de opções, as configurações da primeira linha são determinadas com a chave row{1}. Então é desnecessário escrever \color{white}três vezes.

A macro \nullé removida.

Os suportes ao redor \TOTAL{scoretotal}são removidos.

No comando \nquestion, {}é adicionado depois \thequestioncountpara que os parênteses não sigam imediatamente após o número.

A macro \l_my_tabular_tlnão foi inicializada com \tl_new:N. Isso agora é feito com \tl_new:N \tblrbody.

O conteúdo é coletado com \tl_build_put_right:Nn. Este processo é iniciado \tl_build_begin:Ne finalizado com \tl_build_end:N.

Não é necessário colocar \setcounter{questioncount}{0}depois \newcounter{questioncount}porque o valor inicial já é 0.

Em vez de usar \newcountere \regtotcounter, a abreviatura \newtotcounterpode ser usada.

Não é necessário definir o comando \TOTAL. Isso é substituído por \total.

O comando \setquestionpointsestá integrado no comando \nquestion. Aqui, \arabic{questioncount}é substituído por \thequestioncount. Para \setcounter, \expandede \noexpandsão desnecessários.

Depois de \SetCell, um &é adicionado porque usando \SetCell, as células omitidas não devem ser removidas.

insira a descrição da imagem aqui

\documentclass{article}
\usepackage{xcolor}
\usepackage{tabularray}

\usepackage{totcount}

% Stuff for keeping account of questions and their scores
% ........................................................<<<
\newtotcounter{questioncount}
\newtotcounter{scoretotal}

\newcommand{\getquestionpoints}[1]{%
  \ifnum\value{qpoints#1}>0
    \arabic{qpoints#1}%
  \else
    0%
  \fi
}

% ........................................................>>>

% Typesetting questions
% ........................................................<<<
\newcommand{\nquestion}[1]{%
  \stepcounter{questioncount}%
  \expanded{\noexpand\newtotcounter{qpoints\thequestioncount}}%
  \setcounter{qpoints\thequestioncount}{#1}%
  \addtocounter{scoretotal}{#1}%
  Question~\thequestioncount{} (#1 marks.)%
}
% ........................................................>>>


\begin{document}
\ExplSyntaxOn
\tl_new:N \tblrbody
\tl_build_begin:N \tblrbody
\int_step_inline:nn { \totvalue { questioncount } }
  {
    \tl_build_put_right:Nn \tblrbody { #1 & \total { qpoints#1 } & \\ \hline }
  }
\tl_build_end:N \tblrbody
\ExplSyntaxOff
\begin{tblr}[expand=\tblrbody]{
  colspec={|ccX[c]|},
  width=0.35\linewidth,
  rowsep=1ex,
  row{1}={bg=black,fg=white,rowsep=0.25ex}
}
\emph{Q}\textnumero & Marks & Grade\\\hline
\tblrbody
\SetCell[c=2]{c} ~TOTAL:~ & & \\\cline[r]{3-3}
 & & \total{scoretotal}\\\hline
\end{tblr}


%%% Make some questions:
\vspace{2cm}
\noindent
\nquestion{10}\\
\nquestion{12}\\
\nquestion{15}


\end{document}

Responder2

Encontrei uma solução onde construo as linhas de uma macro no expl3. Não acho que seja o mais elegante, mas funciona. Estou interessado em dicas/comentários para melhorá-lo, então estou deixando esta resposta sem aceitação para ver se alguém aparece com algo bonito.

\ExplSyntaxOn
\NewDocumentCommand{\ToQ}{}{
  \tl_set:Nn \l_tmpa_tl {
    \SetTblrInner{rowsep=1ex}%
    \begin{tblr}[b]{colspec={|ccX[c]|}, width=0.35\linewidth}
      \SetRow{black, rowsep=0.25ex}
      \color{white}\textit{Q}\textnumero
        & \color{white}Marks
          & \color{white}Grade \\ \hline
  }
  \int_step_inline:nn {\totvalue{questioncount}} {
      \tl_put_right:Nn \l_tmpa_tl { ##1 & \fromaux{qpoints##1} & \null \\ \hline }
  }
  \tl_put_right:Nn \l_tmpa_tl {
      \SetCell[c=2]{c}~TOTAL:~&~\null\\\cline[r]{3-3}
      && {\fromaux{scoretotal}}\\\hline
    \end{tblr}
  }
  \l_tmpa_tl
}
\ExplSyntaxOff

Produz

insira a descrição da imagem aqui

Responder3

A vida não precisa ser tão complicada. Sempre comece especificando como você deseja que seja a entrada e como será a saída. Isso é fornecer uma API.

  1. Você não precisa dos contadores LaTeX2e, use o intmódulo.
  2. Quando você quiser armazenar valores, pense em csname.
  3. Crie uma série de comandos \Q1.. \Qnarmazenando o valor como \Q1-marksse quisesse, você poderia ter um \Q1-questionpara armazenar a pergunta real também.

Aqui está uma solução rápida. Usei tabular, apenas para demonstrar os conceitos. Via de regra tento minimizar os pacotes que utilizo. Modifique para adicionar prefixos aos comandos l3, como é uma boa prática.

saída

\documentclass{article}
%------------------------------------------------------------
% INPUT : \AddQuestion{name}{marks}
% OUTPUT: PrintScoreSheet  
%------------------------------------------------------------
\ExplSyntaxOn
\int_new:N \g_questions_total_int
\clist_new:N \g_questions_clist
\prop_new:N \g_questions_marks_prop
\cs_new_protected:Npn \questions_add:nn  #1 #2
  {
    \clist_gput_right:Nn \g_questions_clist {#1}
    \prop_gput:Nnn \g_questions_marks_prop {#1} {#2}
  }

\cs_new_protected:Npn \questions_printlist:
  {
    \begin{tabular}{lrr}
      \hline
      \clist_map_inline:Nn \g_questions_clist
        {
          \prop_get:NnN \g_questions_marks_prop {##1} \l_tmpa_tl
          \int_gadd:Nn \g_questions_total_int { \l_tmpa_tl } 
           ##1  &  \use:c{##1-marks} &\\  %Row
        }
      \\\noalign{\vskip-10pt}
      \cline{2-3} 
      Total & \int_use:N\g_questions_total_int &\\ 
      \hline
    \end{tabular}
  }
\let\AddQuestion\questions_add:nn
\let\PrintScoreSheet\questions_printlist:

\ExplSyntaxOff
\begin{document}
\AddQuestion {Q1}{10}
\AddQuestion {Q2}{20}
\AddQuestion {Q3}{5}
\AddQuestion{Q4}{5}
\PrintScoreSheet
\end{document}

informação relacionada