循环和字符 &

循环和字符 &

我找不到此代码中的问题

\documentclass{article}

\newcount\mt
\newcount\mtt
\newcommand{\mttab}[2]{%
    \mt=0
    \loop
        \advance\mt by 1
        \mtt=1
        {\loop
           \advance\mtt by 1
           &
          \ifnum\mtt<#2
    \repeat}
    \\
    \ifnum\mt<#1
    \repeat
}
\begin{document}

\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3  \\
\hline
\mttab{15}{3}
\end{tabular}

\end{document}

编辑

这个问题的目的是以最简单的方式创建一个空表格

答案1

回答之前如何,没有唯一的答案(你的解决方案是一个很好的答案),我会尝试记录为什么你的初次尝试失败了。这些知识可能有助于分析更复杂的情况。

首先,我记得关键点是你正试图从表格内部扩展一些宏。

  1. 表格单元格会创建组,因此所有分配都应设为全局。因此,我建议在此使用 LaTeX 计数器,因为在 LaTeX 中,它们始终是全局管理的。

  2. 无法在表格单元格内部使用预定义的 TeX 分隔宏(具有诸如 的定义\def\foo #1\repeat{stuff with #1}),如果#1将获得可见的&: \foo A & B\repeat C除了if\foo是单元格中的第一个内容(意味着之前没有出现任何不可扩展的内容,如 a\relax或 a\def或 a \setcounter)。

修复:

  1. 如果你需要做出影响其他单元格的定义或分配,则必须将它们设为全局的,

  2. 如果您需要将其&作为分隔宏的一部分来获取,则必须以某种方式隐藏它,通常在括号组内,或者以\TAB先前\def\TAB{&}或类似\firstofone{&} ( \def\firstofone #1{#1}) 的形式书写。

如果我们按照这些要求修复您原来的代码,是否可以解决问题?

还没有,因为 LaTeX 循环构造当地的 \def

$ latexdef loop

\loop:
\long macro:#1\repeat ->\def \iterate {#1\relax \expandafter \iterate \fi }\iterate \let \iterate \relax 

你的自己的答案给出了正确的解决方法:在单个单元格中使用所有内容,完全通过 来准备宏和要插入的标记\g@addto@macro。这有效地解决了您的问题,并且您可以使用\count而不是计数器。上面的第 2 点呢?好吧,&被适当地隐藏在 中\emttok{&}(这对于外部循环来说不是必需的,但如果没有它,内部循环\loop就会看到裸露的&)。

让我简要讨论一下另一种方法,它不通过构造 来进行\mttok,而是尝试逐行逐单元格地工作。最优雅的是一些可扩展的循环,但让我们首先坚持使用 LaTeX \loop,看看可以做些什么。

首先我们需要将其设为 的全局定义\iterate。但是我们马上就遇到了嵌套的问题。使用这种方法,唯一的方法是定义两个不同的循环宏,例如\gloop\ggloop。在这样做之前,还有更多问题需要提及。

在 的定义中, \relax后面的 存在问题。例如,这将执行。但这会带来灾难性的后果,因为会正确终止测试,但它会保留在标记流中,并且在 之后, 会强制开始新的一行。因此,在表格底部会出现一个空的部分行的问题。我们必须摆脱。我们可以用空格替换它以确保终止测试,或者我们确保始终使用自终止数字,将约束移到调用方。完成所有这些后,我们最终得到了这种方法:#1\iterate\ifnum 1=1\relax\expandafter\iterate\fi\relax\\\relax\ifnum\ifnum

\documentclass{article}

\newcommand*\TAB{&}

% alternative to LaTeX's loop, for use in contexts were definitions
% must be made global to survive

% Thus we do global things, and we use distinct terminators to allow nesting.
% Careful that tests at the end of #1 must be self-terminating

\long\def\gloop #1\grepeat 
    {\gdef \giterate {#1\expandafter \giterate \grepeat }\giterate  }

\long\def\ggloop #1\ggrepeat 
    {\gdef \ggiterate {#1\expandafter \ggiterate \ggrepeat }\ggiterate  }

\let\grepeat\fi
\let\ggrepeat\fi

\newcounter{mt}
\newcounter{mtt}

\newcommand{\mttab}[2]{%
    \setcounter{mt}{0}%
    \gloop
      \stepcounter{mt}% careful with spaces from line endings
      \setcounter{mtt}{1}%
      (\themt)     % to see what happens
        \ggloop 
           \themtt % again to see where we are
           \stepcounter{mtt}%
           \TAB
        \ifnum #1>\value{mtt}%
        \ggrepeat 
      \themtt
      \\
    \ifnum #2>\value{mt}%
    \grepeat
}

\begin{document}
\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3\\
\hline
\mttab{3}{15}
\hline
\end{tabular}

\end{document}

这会产生(我知道您要求空单元格,但我添加了内容以使事物可视化):

在此处输入图片描述

更优雅的方式是使用可扩展循环。LaTeX 有一个这样的循环:\@whilesw这个答案解释了如何使用它。这里给出以下方法:

\documentclass{article}

\newcounter{mt}
\newcounter{mtt}

% recycling the \@whilesw of LaTeX into a num test
% \ifnum tests must be properly self-terminating
% I define the \xwhilenum slightly differently from my earlier use of it
% https://tex.stackexchange.com/a/142562/4686

\makeatletter
  \newcommand*{\xwhilenum}[1]{\@whilesw{\ifnum #1}\fi }
\makeatother

% for reference, the LaTeX kernel definitions:
%\@whilesw:
%\long macro:#1\fi #2->#1#2\@iwhilesw {#1#2}\fi \fi 
%\@iwhilesw:
%\long macro:#1\fi ->#1\expandafter \@iwhilesw \else \@gobbletwo \fi {#1}\fi

\newcommand{\mttab}[2]{%
    \setcounter{mt}{#2}%
    \xwhilenum{\value{mt}>0 }% space after 0 is important
    {% must use braces here
      \addtocounter{mt}{-1}%
      \setcounter{mtt}{#1}%
      (\themt)     % to see where we are
      \xwhilenum{\value{mtt}>1 }% careful, N cells = N-1 &
      {%
         \themtt % again to see where we are
         \addtocounter{mtt}{-1}%
          &
      }% end of inner \xwhilenum
      \themtt
      \\
    }% end of external \xwhilenum
}

\begin{document}
\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3\\
\hline
\mttab{3}{15}
\hline
\end{tabular}
\end{document}

它给出(为了改变,我向下数):

在此处输入图片描述

更优雅的方式也许是完全不使用计数器:借助 可以进行纯可扩展循环\numexpr。但我的答案已经有点长了,因此我就到此为止。

答案2

根据标题生成多行空单元格非常容易expl3:只需嵌套两个\prg_replicate:nn循环。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\emptytab}{mm}
 {% #1 is the comma separated list of headers, #2 the number of empty rows
  \touhami_emptytab:nn { #1 } { #2 }
 }

\int_new:N \l_touhami_emptytab_cols_int
\clist_new:N \l_touhami_emptytab_headers_clist

\cs_new_protected:Npn \touhami_emptytab:nn #1 #2
 {
  % store the headers in a clist
  \clist_set:Nn \l_touhami_emptytab_headers_clist { #1 }
  % count the number of headers
  \int_set:Nn \l_touhami_emptytab_cols_int { \clist_count:n { #1 } }
  % make a tabular
  \begin{tabular}{|*{\l_touhami_emptytab_cols_int}{c|}}
  \hline
  \clist_use:Nn \l_touhami_emptytab_headers_clist { & } \\ \hline
  \prg_replicate:nn { #2 } % repeat #2 times
   {
    % produce <number of cols - 1> cell separators
    \prg_replicate:nn { \l_touhami_emptytab_cols_int - 1 } { & }
    \\ \hline
   }
  \end{tabular}
 }
\ExplSyntaxOff

\begin{document}

\emptytab{col1,col2,col3}{15}\qquad
\emptytab{col1,col2}{3}

\end{document}

在此处输入图片描述

答案3

这是一个解决方案

\documentclass{article}
\newcommand{\mttok}{}
\newcount\mt
\newcount\mtt
\makeatletter
\newcommand{\emttok}[1]{\g@addto@macro{\mttok}{#1}}
\makeatother
\newcommand{\mttab}[2]{%
\renewcommand{\mttok}{}
    \mt=0
    \loop
        \advance\mt by 1
        \mtt=1
        {\loop
           \advance\mtt by 1
           \emttok{&}%
          \ifnum\mtt<#2
    \repeat}%
    \emttok{\\\hline}%
    \ifnum\mt<#1
    \repeat\mttok
}
\begin{document}

\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3  \\
\hline
\mttab{15}{3}
\end{tabular}

\end{document}

答案4

这是一个略有不同的方法。我坚持你的\loop方法,尽管我确实依赖etex\unexpanded

\documentclass{article}

\makeatletter

\newcommand\mttab[2]{%%
  \ae@build@\ae@table@row{#2}{}{&}{\noexpand\\\noexpand\hline}%%
  \ae@build@\ae@table{#1}{\unexpanded\expandafter{\ae@table@row}}{}{}%%
  \ae@table
}

%% |------------------------------------------|
%% | "item" refers to either "column" or "row"|
%% |------------------------------------------|
%% | #1=commmand sequence to save row/table   |
%% | #2=number of interations to pass through |
%% | #3=content for each item                 |
%% | #4=inter-item content                    |
%% | #5=wrap up content                       |
%% |------------------------------------------|
\newcount\ae@build@cnt
\def\ae@build@#1#2#3#4#5{%%
  \let#1\relax
  \ae@build@cnt=0
  \loop
    \advance\ae@build@cnt by 1
    \ifx#1\relax
     \xdef#1{#3}%%
    \else
      \xdef#1{\unexpanded\expandafter{#1}#4#3}%%
    \fi
  \ifnum\ae@build@cnt<#2
  \repeat
  \xdef#1{\unexpanded\expandafter{#1}#5}%%
}

\makeatother

\begin{document}

\begin{tabular}{|c|c|c|}
\hline
col1 & col2 & col3  \\
\hline
\mttab{15}{3}
\end{tabular}

\end{document}

什么是好的关于这种方法,同一个宏既处理行的创建,也处理表其余部分的构建:不需要嵌套循环,除非隐含地

这是一个相当不同的版本,在这里您传递环境的前言tabular、第一行和行数。您不需要指定列数。

\documentclass{article}

\makeatletter

%% #1=preamble for tabular: should be simple "l", "r", or "c" separated only by "|" 
%% #2=formatting for the first row of the table                                     
%% #3=number of empty rows to generate                                              
\newcommand\mttab[3]{%%
  \ae@column@cnt=0 %%
  \ae@count@columns#1\relax%%
  \ae@build@\ae@table@row{\the\ae@column@cnt}{}{&}{\noexpand\\\noexpand\hline}%%
  \ae@build@\ae@table{#3}{\unexpanded\expandafter{\ae@table@row}}{}{}%%
  \ae@prepend@begin@table@\ae@table{#1}{#2}%%
  \ae@append@end@table\ae@table
  \ae@table
}

%% |-------------------------------------------|
%% | "item" refers to either "column" or "row" |
%% |-------------------------------------------|
%% | #1=commmand sequence to save row/table    |
%% | #2=number of interations to pass through  |
%% | #3=content for each item                  |
%% | #4=inter-item content                     |
%% | #5=wrap up content                        |
%% |-------------------------------------------|
\newcount\ae@build@cnt
\def\ae@build@#1#2#3#4#5{%%
  \let#1\relax
  \ae@build@cnt=0
  \loop
    \advance\ae@build@cnt by 1 %%
    \ifx#1\relax
     \xdef#1{#3}%%
    \else
      \xdef#1{\unexpanded\expandafter{#1}#4#3}%%
    \fi
  \ifnum\ae@build@cnt<#2
  \repeat
  \xdef#1{\unexpanded\expandafter{#1}#5}%%
}

\def\ae@prepend@begin@table@#1#2#3{%%
  \xdef#1{\noexpand\begin{tabular}{#2}%%
          \unexpanded{#3}%%
          \unexpanded\expandafter{#1}}}

\def\ae@append@end@table#1{%%
  \xdef#1{\unexpanded\expandafter{#1}\noexpand\end{tabular}}%%
  }

%% This macro moves through the preable for the tabular
%% and counts the number of tokens which are not "|".
\newcount\ae@column@cnt
\def\ae@count@columns#1{%%
  \def\ae@continue{}%%
  \ifx#1\relax
  \else
    \let\ae@continue\ae@count@columns
    \ifx#1|
    \else
      \advance\ae@column@cnt by 1 %%
    \fi
  \fi
  \ae@continue}

\makeatother

\begin{document}

\mttab{|c|c|c|}{\hline col1 & col2 & col3 \\\hline}{15}

\end{document}

成功的关键是计算列数的宏。它假设除了列说明符之外lr、 或c|出现在tabular环境的前言中。

相关内容