如题,希望实现如下的表格的自动化排版。
代码越简洁、越高度自动化、越具有强拓展性越好。

划去线的逻辑不必与此相同(我觉得涂色似乎更显眼,在tabularray框架里应该也更容易实现),能体现不同的条件判断即可。

由于我也会试试,这里暂不给出MWE.
用 tikz 实现。
\documentclass[tikz,border=1cm]{standalone}
\begin{document}
\begin{tikzpicture}[
cell/.style={
draw,
line width=0.8pt,
minimum width=7.2mm,
minimum height=6mm,
inner sep=0pt,
font=\small
}
]
\def\inset{1mm}
\foreach \x in {1,...,100} {
\pgfmathsetmacro{\row}{int((\x-1)/10)}
\pgfmathsetmacro{\col}{int((\x-1) - 10*\row)}
\node[cell] (N\x) at ({7.2mm*\col},{-6mm*\row}) {\x};
\ifnum\x=2\relax\else
\ifnum\x=3\relax\else
\ifnum\x=5\relax\else
\ifnum\x=7\relax\else
\pgfmathtruncatemacro{\mii}{mod(\x,2)}
\ifnum\mii=0
\draw[thick]
([xshift=\inset,yshift=-\inset]N\x.north west) --
([xshift=-\inset,yshift=\inset]N\x.south east);
\else
\pgfmathtruncatemacro{\miii}{mod(\x,3)}
\ifnum\miii=0
\draw[thick]
([xshift=-\inset,yshift=-\inset]N\x.north east) --
([xshift=\inset,yshift=\inset]N\x.south west);
\else
\pgfmathtruncatemacro{\mv}{mod(\x,5)}
\ifnum\mv=0
\draw[thick]
([xshift=\inset,yshift=-\inset]N\x.north west) --
([xshift=-\inset,yshift=\inset]N\x.south east);
\draw[thick]
([xshift=-\inset,yshift=-\inset]N\x.north east) --
([xshift=\inset,yshift=\inset]N\x.south west);
\else
\pgfmathtruncatemacro{\mvii}{mod(\x,7)}
\ifnum\mvii=0
\draw[thick]
([xshift=\inset]N\x.west) --
([xshift=-\inset]N\x.east);
\fi
\fi
\fi
\fi
\fi\fi\fi\fi
}
\end{tikzpicture}
\end{document}
基于tabularray的一个自动化方案...
\documentclass{article}
\usepackage{fourier}
\usepackage{xcolor}
\usepackage{tabularray}
\UseTblrLibrary{functional}
\IgnoreSpacesOn
\prgNewFunction \makeHundredCells { } {
\tlSet \lTmpaTl { }
\intStepOneInline {1} {100} {
\intCaseF { ##1 } {
{ 2 } { }
{ 3 } { }
{ 5 } { }
{ 7 } { }
} {
\intCompareTF { \intMathMod{##1}{2} } = { 0 } {
\tlPutRight \lTmpaTl { \SetCell{bg=magenta!50} }
}{
\intCompareTF { \intMathMod{##1}{3} } = { 0 } {
\tlPutRight \lTmpaTl { \SetCell{bg=cyan!50} }
}
{
\intCompareTF { \intMathMod{##1}{5} } = { 0 } {
\tlPutRight \lTmpaTl { \SetCell{bg=violet!50} }
}
{
\intCompareT { \intMathMod{##1}{7} } = { 0 } {
\tlPutRight \lTmpaTl { \SetCell{bg=orange!50} }
}
}
}
}
}
\intCompareTF { ##1 } = { 1 } {
\tlPutRight \lTmpaTl { }
}{
\tlPutRight \lTmpaTl { ##1 }
}
\intCompareTF { \intMathMod{##1}{10} } = { 0 } {
\tlPutRight \lTmpaTl { \\ }
}{
\tlPutRight \lTmpaTl { & }
}
}
\prgReturn { \expValue \lTmpaTl }
}
\IgnoreSpacesOff
\begin{document}
\begin{tblr}[evaluate=\makeHundredCells]{
colspec = {*{10}{X[c]}},
hlines, vlines,
rows = {m, ht=3em},
}
\makeHundredCells
\end{tblr}
\end{document}或者等价的,用逻辑划分更清晰的evaluate = \makeTableBody和process = \myTblrProcessor分别控制内容和样式
\documentclass{article}
\usepackage{fourier}
\usepackage{xcolor}
\usepackage{tabularray}
\UseTblrLibrary{functional}
\IgnoreSpacesOn
\tlNew \lBodyTl
\tlNew \lCellValueTl
\prgNewFunction \makeTableBody { } {
\tlClear \lBodyTl
\intStepOneInline {1} {100} {
\tlPutRight \lBodyTl { ##1 }
\intCompareTF { \intMathMod{##1}{10} } = { 0 } {
\tlPutRight \lBodyTl { \\ }
}{
\tlPutRight \lBodyTl { & }
}
}
\prgReturn { \expValue \lBodyTl }
}
\prgNewFunction \myTblrProcessor { } {
\intStepOneInline {1} {\arabic{rowcount}} {
\intStepOneInline {1} {\arabic{colcount}} {
\tlSet \lCellValueTl { \cellGetText {##1} {####1} }
\intCompareT { \lCellValueTl } = { 1 } {
\cellSetText {##1} {####1} { }
}
\intCaseF { \lCellValueTl } {
{ 2 } { } { 3 } { } { 5 } { } { 7 } { }
} {
\intCompareTF { \intMathMod{\lCellValueTl}{2} } = { 0 } {
\cellSetStyle {##1} {####1} { bg=magenta!50 }
}{
\intCompareTF { \intMathMod{\lCellValueTl}{3} } = { 0 } {
\cellSetStyle {##1} {####1} { bg=cyan!50 }
}{
\intCompareTF { \intMathMod{\lCellValueTl}{5} } = { 0 } {
\cellSetStyle {##1} {####1} { bg=violet!50 }
}{
\intCompareT { \intMathMod{\lCellValueTl}{7} } = { 0 } {
\cellSetStyle {##1} {####1} { bg=orange!50 }
}
}
}
}
}
}
}
}
\IgnoreSpacesOff
\begin{document}
\begin{tblr}[
evaluate = \makeTableBody
]{
colspec = {*{10}{X[c]}},
hlines, vlines,
rows = {m, ht=3em},
process = \myTblrProcessor
}
\makeTableBody
\end{tblr}
\end{document}
基于 asymptote 的一个回答:
settings.outformat="pdf";
unitsize(1cm);
import primetable;
int nums=100; int cols=10; real dx=1; real dy=0.6;
markPrimeTable(nums,cols,dx,dy,lineMarkers);
newpage();
markPrimeTable(nums,cols,dx,dy,ploymarkers(getPrimeFactors(nums).length));
newpage();
labelPrimeTable(nums,cols,dx,dy,randrgbs(getPrimeFactors(nums).length));


还可以:
settings.outformat="pdf";
unitsize(1cm);
import primetable;
int nums=1000; int cols=50; real dx=1; real dy=0.6;
// markPrimeTable(nums,cols,dx,dy,lineMarkers);
// newpage();
markPrimeTable(nums,cols,dx,dy,ploymarkers(getPrimeFactors(nums).length));
newpage();
labelPrimeTable(nums,cols,dx,dy,randrgbs(getPrimeFactors(nums).length));

primetable.asy 如下(包含结构体)
private from math access grid;
/*======== 画笔/标注 =========*/
pen[] randrgbs(int n) {
return sequence(new pen(int){return rgb(unitrand(),unitrand(),unitrand());},n);
}
real defaultangle=45; real defaultxscale=0.5; real defaultyscale=10;
pen NWLine(real angle=defaultangle, real xscale=defaultxscale, real yscale=defaultyscale) {
return makepen(rotate(angle)*scale(xscale,yscale)*polygon(4));
}
pen NELine(real angle=-defaultangle, real xscale=defaultxscale, real yscale=defaultyscale) {
return makepen(rotate(angle)*scale(xscale,yscale)*polygon(4));
}
pen NWNELine(real angle=defaultangle, real xscale=defaultxscale, real yscale=defaultyscale) {
path NWLine=rotate(angle)*scale(xscale,yscale)*polygon(4);
path NELine=rotate(-angle)*scale(xscale,yscale)*polygon(4);
real[][] inters=intersections(NWLine,NELine);
path p=subpath(NELine,inters[1][1],inters[0][1]);
real[][] inters=intersections(NELine,NWLine);
path q=subpath(NWLine,inters[2][1],inters[3][1]);
return makepen(buildcycle(p,q,rotate(180)*p,rotate(180)*q));
}
pen HLine(real xscale=defaultxscale, real yscale=defaultyscale) {
return makepen(rotate(90)*scale(xscale,yscale)*polygon(4));
}
pen ploypen(int N, real angle=0, real xscale=1, real yscale=1) {
return makepen(rotate(angle)*scale(xscale,yscale)*polygon(N));
}
pen NWLine=NWLine();
pen NELine=NELine();
pen NWNELine=NWNELine();
pen HLine=HLine();
pen[] lineMarkers=new pen[] {NELine,NWLine,NWNELine,HLine};
pen[] ploymarkers(int n, real scalefactor=6) {
return sequence(new pen(int i) {return scale(scalefactor)*ploypen(i+3)+opacity(0.4);},n);
}
/*======== 素数判断 =========*/
// 判断是否是素数
bool isPrime(int n) {
if (n <= 1) { return false; }
else if ( n == 2) { return true; }
else if ( n % 2 == 0 ) { return false; }
else {
for (int i=3; i<= ceil(sqrt(n)); i+=2) {
if (n % i == 0) {
return false;
}
}
return true;
}
}
// 素因子
int[] getPrimeFactors(int n) {
int[] op;
for (int i=1; i<=ceil(sqrt(n)); ++i) {
if (isPrime(i)) { op.push(i); }
}
return op;
}
/*======== 标记/标注表格 =========*/
// 标注数字
void labelnumber(int i, int cols, real dx, real dy, pen p=currentpen) {
label("$"+(string)i+"$",scale(dx,dy)*((i-1) % cols, -quotient(i-1,cols)),p);
}
// 标记数字
void marknumber(int i, int cols, real dx, real dy, pen p=currentpen) {
draw("$"+(string)i+"$",scale(dx,dy)*((i-1) % cols, -quotient(i-1,cols)),p);
}
// 绘制表格
void drawTable(int nums, int cols, real dx, real dy) {
int rows=ceil(nums/cols);
add(shift(0,-rows*dy)*shift(-0.5dx,0.5dy)*scale(dx,dy)*grid(cols,rows));
}
// 标注素数表格
void markPrimeTable(int nums, int cols, real dx, real dy, pen[] pens) {
int[] primes=getPrimeFactors(nums);
drawTable(nums,cols,dx,dy);
for (int i=2; i<=nums; ++i) {
bool[] b=sequence(new bool(int j) {return i % primes[j] ==0; }, primes.length);
int j=find(b);
if (j == -1) {
marknumber(i,cols,dx,dy,linewidth(0));
} else {
marknumber(i,cols,dx,dy,(i == primes[j] ? nullpen : pens[j]));
}
}
}
// 标注素数表格
void labelPrimeTable(int nums, int cols, real dx, real dy, pen[] pens) {
int[] primes=getPrimeFactors(nums);
drawTable(nums,cols,dx,dy);
for (int i=2; i<=nums; ++i) {
bool[] b=sequence(new bool(int j) {return i % primes[j] ==0; }, primes.length);
int j=find(b);
if (j == -1) {
labelnumber(i,cols,dx,dy,linewidth(0));
} else {
labelnumber(i,cols,dx,dy,(i == primes[j] ? nullpen : pens[j]));
}
}
}
struct primetable {
int nums;
int cols;
int rows;
real dx;
real dy;
int[] primefactors;
void operator init(int nums, int cols, real dx, real dy) {
this.nums=nums;
this.cols=cols;
this.rows=ceil(nums / cols);
this.dx=dx;
this.dy=dy;
this.primefactors=getPrimeFactors(nums);
}
}
void mark(primetable pt, pen[] pens) {
drawTable(pt.nums,pt.cols,pt.dx,pt.dy);
for (int i=2; i<=pt.nums; ++i) {
bool[] b=sequence(new bool(int j) {return i % pt.primefactors[j] == 0; }, pt.primefactors.length);
int j=find(b);
if (j == -1) {
marknumber(i,pt.cols,pt.dx,pt.dy,linewidth(0));
} else {
marknumber(i,pt.cols,pt.dx,pt.dy,(i == pt.primefactors[j] ? nullpen : pens[j]));
}
}
}
void label(primetable pt, pen[] pens) {
drawTable(pt.nums,pt.cols,pt.dx,pt.dy);
for (int i=2; i<=pt.nums; ++i) {
bool[] b=sequence(new bool(int j) {return i % pt.primefactors[j] == 0; }, pt.primefactors.length);
int j=find(b);
if (j == -1) {
marknumber(i,pt.cols,pt.dx,pt.dy,linewidth(0));
} else {
marknumber(i,pt.cols,pt.dx,pt.dy,(i == pt.primefactors[j] ? nullpen : pens[j]));
}
}
}利用结构体 primetable 可以较为简洁的绘出
settings.outformat="pdf";
unitsize(1cm);
import primetable;
int nums=100; int cols=10; real dx=1; real dy=0.6;
primetable pt=primetable(nums,cols,dx,dy);
mark(pt,lineMarkers);
newpage();
defaultangle=aTan(dx/dy); defaultyscale=15;
pen[] lineMarkers={NELine(),NWLine(),NWNELine(),HLine()};
mark(pt,lineMarkers); newpage();
real angle=aTan(dx/dy); real xscale=0.5; real yscale=15;
pen[] mylineMarkers2={NELine(-angle,xscale,yscale),
NWLine(angle,xscale,yscale),
NWNELine(angle,xscale,yscale),
HLine(xscale,yscale)};
mark(pt,mylineMarkers2); newpage();
mark(pt,ploymarkers(pt.primefactors.length)); newpage();
label(pt,randrgbs(pt.primefactors.length));



