我想编写一个用于编写民意调查的命令。我的计划是:
\poll[<sumofvotes>]{<Poll title>}{
OptionA: <Votes for A>
OptionB: <Votes for B>*
OptionC: <Votes for C>
OptionD: <Votes for D>
}
其中星号表示该选项获胜。
此命令应创建一个表格,其中一列是冒号之前的部分,:
另一列是冒号之后的部分。如果冒号以星号结尾,则此行将突出显示(使用背景颜色),并且文本“Winning Option”应附加到此行。
我还想将所有投票加起来,如果投票总数不等于参数,则打印错误sumofvotes
- 如果存在这样的参数。
但是,我遇到了两个问题:
- 如何在换行符上拆分文本?(尝试过
\readlist*
但\setsepchar{\n}
没有成功 - 如何引用乳胶中的换行符?) - 我如何生成表格?(
\foreachitem
在正文中使用tabular
似乎不起作用)
答案1
它可以清理。我只是想展示一下可以采取的基本方法。
这里的一个关键是\foreachitem
列表的循环变量必须扩展因为它正在被塞满放入tabular
数组中。为此,我发现使用标记列表更容易。
\documentclass{article}
\usepackage{listofitems,xcolor}
\newcommand\winstyle{\bfseries\color{red}}
\newtoks\tabtoks
\newcommand\addtotabtoks[1]{\tabtoks\expandafter{\the\tabtoks#1}}
\newcommand\xaddtotabtoks[1]{\expandafter\addtotabtoks\expandafter{#1}}
\newcommand\xxaddtotabtoks[1]{\expandafter\xaddtotabtoks\expandafter{#1}}
\newcommand\poll[3][0]{%
\setsepchar{\\/:/*}%
\tabtoks{}%
\readlist*\votes{#3}%
\def\votetally{0}%
\foreachitem\x\in\votes[]{%
\ifnum\listlen\votes[\xcnt,2]>1\relax\addtotabtoks{\winstyle}\fi%
\xxaddtotabtoks{\votes[\xcnt,1] & }%
\ifnum\listlen\votes[\xcnt,2]>1\relax\addtotabtoks{\winstyle}\fi%
\xxaddtotabtoks{\votes[\xcnt,2,1]&}%
\ifnum\listlen\votes[\xcnt,2]>1\relax
\addtotabtoks{\winstyle~Winning Option\\}\else
\addtotabtoks{\\}\fi%
\edef\votetally{\the\numexpr\votetally+\votes[\xcnt,2,1]\relax}
}%
\centerline{\textsc{#2} (\votetally{} votes)}%
\centerline{\begin{tabular}{lrc}\the\tabtoks\end{tabular}}%
}
\begin{document}
\poll{Poll Title}{
Option A: 6\\
Option B: 11*\\
Option C: 3
}
\end{document}
您将看到我对可选变量没有执行任何操作,但您可以用并测试比较和#1
来结束宏,如果它们不同意,则发出错误消息。\ifnum
#1
\votetally
答案2
使用结束符作为分隔符对于 TeX 来说是一个很大的问题,因此我建议使用标准行终止符\\
。
\documentclass{article}
\usepackage{xparse}
\usepackage[table,dvipsnames]{xcolor}
\usepackage{booktabs}
\colorlet{winning}{SkyBlue}
\ExplSyntaxOn
\NewDocumentCommand{\poll}{o m m}
{
\tobi_poll:nn { #2 } { #3 }
\IfValueT{#1}
{
\tobi_poll_check:n { #1 }
}
}
\seq_new:N \l_tobi_poll_votes_seq
\int_new:N \l_tobi_poll_votes_total_int
\tl_new:N \l__tobi_poll_table_tl
\tl_new:N \l__tobi_poll_vote_tl
\seq_new:N \l__tobi_poll_row_seq
\msg_new:nnnn { poll } { not-matching }
{
Votes~don't~match~(#2,~expected~#1)
}
{
The~total~number~of~votes~should~be~#1,~you~have~#2
}
\cs_generate_variant:Nn \seq_set_split:Nnn { NV }
\cs_new_protected:Nn \tobi_poll:nn
{
\seq_set_split:Nnn \l_tobi_poll_votes_seq { \\ } { #2 }
\tl_clear:N \l__tobi_poll_table_tl
\int_zero:N \l_tobi_poll_votes_total_int
\seq_map_function:NN \l_tobi_poll_votes_seq \__tobi_poll_row:n
\__tobi_poll_maketable:n { #1 }
}
\cs_new_protected:Nn \__tobi_poll_row:n
{
% the sequence will contain the option as item 1 and the votes as item 2
\seq_set_split:NVn \l__tobi_poll_row_seq \c_colon_str { #1 }
% store the votes, we need to look for *
\tl_set:Nx \l__tobi_poll_vote_tl
{
\seq_item:Nn \l__tobi_poll_row_seq { 2 }
}
\str_if_eq:eeTF { * } { \str_item:Nn \l__tobi_poll_vote_tl { -1 } }
{% remove the trailing asterisk
\tl_set:Nx \l__tobi_poll_vote_tl
{
\tl_range:Nnn \l__tobi_poll_vote_tl { 1 } { -2 }
}
\tl_put_right:Nx \l__tobi_poll_table_tl
{
\exp_not:N \rowcolor{winning} % background color
\seq_item:Nn \l__tobi_poll_row_seq { 1 } &
\l__tobi_poll_vote_tl
& (Winning ~ option) % with the winning option
\exp_not:N \\
}
}
{% no asterisk
\tl_put_right:Nx \l__tobi_poll_table_tl
{
\seq_item:Nn \l__tobi_poll_row_seq { 1 } &
\l__tobi_poll_vote_tl
\exp_not:N \\
}
}
% add up the votes
\int_add:Nn \l_tobi_poll_votes_total_int { \l__tobi_poll_vote_tl }
}
\cs_new_protected:Nn \tobi_poll_check:n
{
\int_compare:nF { #1 = \l_tobi_poll_votes_total_int }
{
\msg_error:nnxx { poll } { not-matching }
{ #1 }
{ \int_to_arabic:n { \l_tobi_poll_votes_total_int } }
}
}
\cs_new_protected:Nn \__tobi_poll_maketable:n
{
\begin{tabular}{lrl}
\toprule
\multicolumn{3}{c}
{
#1~(\int_to_arabic:n {\l_tobi_poll_votes_total_int}~votes)} \\
\midrule
\l__tobi_poll_table_tl
\bottomrule
\end{tabular}
}
\ExplSyntaxOff
\begin{document}
\poll{Example poll}{
A: 12 \\
B: 15* \\
C: 10 \\
D: 8
}
\qquad
\poll[45]{Example poll}{
A: 12 \\
B: 15* \\
C: 10 \\
D: 8
}
\bigskip
\poll[120]{Example poll}{
A: 12 \\
B: 15* \\
C: 10 \\
D: 8
}
\end{document}
最后\poll
告诉用户
! Package poll Error: Votes don't match (45, expected 120)
For immediate help type H <return>.
...
l.128 }
? h
The total number of votes should be 120, you have 45
?