在 longtabu 中求和不起作用

在 longtabu 中求和不起作用

找到解决方案后,longtable我发现我需要,longtabu因为我想使用X列。

但是,使用longtabu总和会出错,显示正确值的两倍。使用列时,X它会添加更多内容。

这是因为该表被编译多次,因此也多次运行添加部分吗? 还有其他方法可以解决这个问题吗?expl3或者我只需要使用longtable然后手动计算X列以使表填满整个textwidth

\documentclass[parskip=full]{scrlttr2}

\renewcommand\familydefault{\sfdefault}
\usepackage[defaultsans, scale=0.9]{opensans}

\usepackage[check-declarations]{expl3}

\ExplSyntaxOn

\NewDocumentCommand{\newtime}{m}
{
    \fp_new:c { g_timecalc_time_#1_fp }
}
\newtime{default} % initialize one

\NewDocumentCommand{\resettime}{O{default}}{
    \fp_gset:cn { g_timecalc_time_#1_fp } { \fpeval{0} }
}

% \addtime* will also show the added time
\NewDocumentCommand{\addtime}{sO{default}mm}
{
    \timecalc_time_add:nnn { #2 } { #3 } { #4 }

    % with \addtime* also show what's been added    
    \IfBooleanT{#1}{#3\,\textup{#4}}
}

\NewDocumentCommand{\strtime}{O{default}m}
{
    \timecalc_time_str:nn { #1 } { #2 }
}

\int_new:N \l__timecalc_time_days_int
\int_new:N \l__timecalc_time_hours_int
\int_new:N \l__timecalc_time_minutes_int

\cs_new_protected:Nn \timecalc_time_add:nnn
{
    \str_case:nnF { #3 }
    {
        {h}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 * 60 } }
        {d}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 * 60 * 24 } }
        {min}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 } }
    }
    {\ERROR}
}

\cs_new_protected:Nn \int_div_mod:nnn {
    \int_set:Nn #3 
        { \int_div_truncate:nn { #1 } { #2 } }
    
    \int_set:Nn #1
        { \int_mod:nn { #1 } { #2 } }
    }

\cs_new_protected:Nn \timecalc_time_str:nn
{
    % round to an integral number of minutes
    \int_set:Nn \l__timecalc_time_minutes_int
        { \fp_eval:n { round( \fp_use:c { g_timecalc_time_#1_fp }, 0 ) } }

    \bool_if:nTF
    {
        \str_if_eq_p:nn{#2}{d}
    }
    {%yes
        % compute and remove the number of days
        \int_div_mod:nnn{\l__timecalc_time_minutes_int}{1440}{\l__timecalc_time_days_int}
        
        % now print the days
        \int_compare:nTF { \l__timecalc_time_days_int > 0 }
        { \int_eval:n { \l__timecalc_time_days_int }\,\textup{d}\; }
        {% no days
            \unkern
        }
    }{} % no

    \bool_if:nTF
    {
        \str_if_eq_p:nn{#2}{h} ||
        \str_if_eq_p:nn{#2}{d} 
    }
    {%yes
        % compute and remove the number of hours
        \int_div_mod:nnn{\l__timecalc_time_minutes_int}{60}{\l__timecalc_time_hours_int}
        
        % now print the hours
        \int_compare:nTF { \l__timecalc_time_hours_int > 0 }
        {
            \int_eval:n { \l__timecalc_time_hours_int }\,\textup{h}\;
        }
        {% no hours
            \unkern
        }       
    }{} % no


    % now print the minutes
    \int_compare:nTF { \l__timecalc_time_minutes_int > 0 }
    {
        \int_eval:n { \l__timecalc_time_minutes_int }\,\textup{min}
    }
    {% no minutes
        0\,min%\unkern
    }
}

\ExplSyntaxOff

\usepackage{longtable}[=v4.13]
\usepackage{tabu}%

\begin{document}
    
    \begin{longtable}{r}
        \textbf{time}
        \endhead
        \addtime*{30}{min}\\
        \addtime*{4}{h}  \\
    \end{longtable}
    
    This leads to a complete time of \strtime{h}.

    \resettime
    Current time: \strtime{h}.
    
    \begin{longtabu}{r}%
        \textbf{time}
        \endhead
        \addtime*{30}{min}\\
        \addtime*{4}{h}  \\
    \end{longtabu}%

    This leads to a complete time of \strtime{h}.   

    \resettime
    Current time: \strtime{h}.
    
    \begin{longtabu}{X}%
        \textbf{time}
        \endhead
        \addtime*{30}{min}\\
        \addtime*{4}{h}  \\ 
    \end{longtabu}%
    
    This leads to a complete time of \strtime{h}.
            
\end{document}

答案1

编辑以统一编码风格并使用 OP 的初始代码

\documentclass[parskip=full]{scrlttr2}

\renewcommand\familydefault{\sfdefault}
\usepackage[defaultsans, scale=0.9]{opensans}

\usepackage[check-declarations]{expl3}



% \newcommand\tabudoonlyatlast{%
%  \ifx\tabu@savecounters\relax\expandafter\@gobble\else\expandafter\@firstofone\fi
% }

\ExplSyntaxOn

% WARNING! THIS USES \tex_relax:D WHICH IS FORBIDDEN (D = DoNotUse)
% But I have no idea what should take its place.
% Not "\relax"? that would be way too lenient. 
\makeatletter
\NewDocumentCommand{\mrCarnivoreprotect}{m}{
    \tl_if_eq:NNTF { \tabu@savecounters } { \tex_relax:D }
                   { }
                   { #1 }
}
\makeatother

\NewDocumentCommand{\newtime}{m}
{
    \fp_new:c { g_timecalc_time_#1_fp }
}
\newtime{default} % initialize one

\NewDocumentCommand{\resettime}{O{default}}{
    \fp_gset:cn { g_timecalc_time_#1_fp } { \fpeval{0} }
}

% \addtime* will also show the added time
\NewDocumentCommand{\addtime}{sO{default}mm}
{
    \timecalc_time_add:nnn { #2 } { #3 } { #4 }

    % with \addtime* also show what's been added    
    \IfBooleanT{#1}{#3\,\textup{#4}}
}

\NewDocumentCommand{\strtime}{O{default}m}
{
    \timecalc_time_str:nn { #1 } { #2 }
}

\int_new:N \l__timecalc_time_days_int
\int_new:N \l__timecalc_time_hours_int
\int_new:N \l__timecalc_time_minutes_int

\cs_new_protected:Nn \timecalc_time_add:nnn
{
    \str_case:nnF { #3 }
    {
        {h}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 * 60 } }
        {d}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 * 60 * 24 } }
        {min}{ \fp_gadd:cn { g_timecalc_time_#1_fp } { #2 } }
    }
    {\ERROR}
}

\cs_new_protected:Nn \int_div_mod:nnn {
    \int_set:Nn #3 
        { \int_div_truncate:nn { #1 } { #2 } }
    
    \int_set:Nn #1
        { \int_mod:nn { #1 } { #2 } }
    }

\cs_new_protected:Nn \timecalc_time_str:nn
{
    % round to an integral number of minutes
    \int_set:Nn \l__timecalc_time_minutes_int
        { \fp_eval:n { round( \fp_use:c { g_timecalc_time_#1_fp }, 0 ) } }

    \bool_if:nTF
    {
        \str_if_eq_p:nn{#2}{d}
    }
    {%yes
        % compute and remove the number of days
        \int_div_mod:nnn{\l__timecalc_time_minutes_int}{1440}{\l__timecalc_time_days_int}
        
        % now print the days
        \int_compare:nTF { \l__timecalc_time_days_int > 0 }
        { \int_eval:n { \l__timecalc_time_days_int }\,\textup{d}\; }
        {% no days
            \unkern
        }
    }{} % no

    \bool_if:nTF
    {
        \str_if_eq_p:nn{#2}{h} ||
        \str_if_eq_p:nn{#2}{d} 
    }
    {%yes
        % compute and remove the number of hours
        \int_div_mod:nnn{\l__timecalc_time_minutes_int}{60}{\l__timecalc_time_hours_int}
        
        % now print the hours
        \int_compare:nTF { \l__timecalc_time_hours_int > 0 }
        {
            \int_eval:n { \l__timecalc_time_hours_int }\,\textup{h}\;
        }
        {% no hours
            \unkern
        }       
    }{} % no


    % now print the minutes
    \int_compare:nTF { \l__timecalc_time_minutes_int > 0 }
    {
        \int_eval:n { \l__timecalc_time_minutes_int }\,\textup{min}
    }
    {% no minutes
        0\,min%\unkern
    }
}

\ExplSyntaxOff

\usepackage{longtable}[=v4.13]
\usepackage{tabu}%

\begin{document}
    
    \begin{longtable}{r}
        \textbf{time}
        \endhead
        \addtime*{30}{min}\\
        \addtime*{4}{h}\\
    \end{longtable}
    
    This leads to a complete time of \strtime{h}.

    \resettime
    Current time: \strtime{h}.
    
    \begin{longtabu}{r}%
        \textbf{time}
        \endhead
        \mrCarnivoreprotect{\addtime*{30}{min}}\\
        \mrCarnivoreprotect{\addtime*{4}{h}}\\
    \end{longtabu}%

    This leads to a complete time of \strtime{h}.   

    \resettime
    Current time: \strtime{h}.
    
    \begin{longtabu}{X}%
        \textbf{time}
        \endhead
        \mrCarnivoreprotect{\addtime*{30}{min}}\\
        \mrCarnivoreprotect{\addtime*{4}{h}}\\ 
    \end{longtabu}%
    
    This leads to a complete time of \strtime{h}.
            
\end{document}

生产

使用 LaTeX3 进行禁忌输出

原始版本

tabu 会进行多次传递(或者可能会),可能 2 次或 3 次,甚至更多(不确定)。我快速查看了它的代码,但没有找到可以查询的条件。但是看起来好像在tabu 初步使用后\tabu@savecounters设置为\relax,并在最终传递时重置(可能是退出组)。因此这样。

\documentclass[parskip=full]{scrlttr2}

\renewcommand\familydefault{\sfdefault}
\usepackage[defaultsans, scale=0.9]{opensans}

\usepackage{longtable}[=v4.13]
\usepackage{tabu}%

\makeatletter
\newcommand\tabudoonlyatlast{%
 \ifx\tabu@savecounters\relax\expandafter\@gobble\else\expandafter\@firstofone\fi
}
\makeatother

\begin{document}
    
\def\x{0}
\def\stepx{\xdef\x{\the\numexpr\x+1}}

Value of x: \x.

    \begin{longtabu}{r}%
        \textbf{time}
        \endhead
        initial x = \x\\
        final x = \tabudoonlyatlast{\stepx}\x
    \end{longtabu}

Value of x: \x.
\end{document}

禁忌输出

话虽如此,我们因此在最终排版过程中修改了输出,并且我们可能会弄乱禁忌测量(例如当我们从 9 变为 10 时)。因此,为了确保万无一失,它应该与在固定宽度框中呈现的变量一起使用。

答案2

回答评论中的问题:longtable在命令中存储辅助文件中每个表的列宽(使用罗马数字):

\gdef \LT@i {\LT@entry 
    {1}{17.00003pt}\LT@entry 
    {1}{27.27783pt}\LT@entry 
    {1}{32.55562pt}\LT@entry 
    {1}{50.88902pt}}
\gdef \LT@ii {\LT@entry 
    {1}{59.45284pt}\LT@entry 
    {1}{91.35826pt}\LT@entry 
    {1}{80.13902pt}}

您可以通过处理此命令来检索列大小,例如在 longtable 之前。您可以自行决定如何处理这些信息。在这里,我仅显示它并绘制规则(请注意,信息仅在第二次编译时存在,因此您需要进行一些测试并回退到第一次编译)。

\documentclass{article}
\usepackage{longtable,tikz}

\makeatletter
\ExplSyntaxOn

\newcommand\shownextlongtablewidth
 {
  \group_begin:
  \int_zero:N\l_tmpa_int
  \tl_if_exist:cT {LT@\int_to_roman:n{\c@LT@tables+1}}
   {
    \par\centering % to center the rules like the longtable
    \cs_set:Npn \LT@entry##1##2
     {
      \int_incr:N\l_tmpa_int
      \typeout{Width~of~column~\int_use:N\l_tmpa_int\c_space_tl~
               in~table~\int_eval:n{\c@LT@tables+1}~is~##2}
      \tikz{\draw[red,<->](0,0)--++(##2,0)}         
     } 
    \tl_use:c{LT@\int_to_roman:n{\c@LT@tables+1}}%
    \par
   }
  \group_end:
 }
\makeatother
\ExplSyntaxOff
\begin{document}
\shownextlongtablewidth 
\begin{longtable}{lcl|l}
a&abc & duck & blubblub
\end{longtable}

\shownextlongtablewidth
\begin{longtable}{l@{\hspace{1cm}}p{3cm}l}
12345&abc & some long word \\
b & xxxxxxx &123
\end{longtable}

\end{document}

日志文件将会显示

Width of column 1 in table 1 is 17.00003pt
Width of column 2 in table 1 is 27.27783pt
Width of column 3 in table 1 is 32.55562pt
Width of column 4 in table 1 is 50.88902pt
Width of column 1 in table 2 is 59.45284pt
Width of column 2 in table 2 is 91.35826pt
Width of column 3 in table 2 is 80.13902pt

在此处输入图片描述

答案3

我建议你使用板状的封装并使用如下代码。

  • 在序言中:

    \usepackage{xltabular}
    \newcolumntype{R}{>{\raggedleft\arraybackslash}X}
    
  • 在文档正文中:

    \begin{xltabular}{\textwidth}{R}
        \textbf{time}
        \endhead
        \addtime*{30}{min}\\
        \addtime*{4}{h} n \\
    \end{xltabular}
    

    其中\addtime是您发布的查询中定义的 L3 语法命令之一。

我忍不住要说,如果该表实际上只包含三行,那么longtableand/orxltabular包的机制在这里就没有很好地部署。

相关内容