将民意调查/投票漂亮地打印为表格的命令(拆分参数值)

将民意调查/投票漂亮地打印为表格的命令(拆分参数值)

我想编写一个用于编写民意调查的命令。我的计划是:

\poll[<sumofvotes>]{<Poll title>}{
OptionA: <Votes for A>
OptionB: <Votes for B>*
OptionC: <Votes for C>
OptionD: <Votes for D>
}

其中星号表示该选项获胜。

此命令应创建一个表格,其中一列是冒号之前的部分,:另一列是冒号之后的部分。如果冒号以星号结尾,则此行将突出显示(使用背景颜色),并且文本“Winning Option”应附加到此行。

我还想将所有投票加起来,如果投票总数不等于参数,则打印错误sumofvotes- 如果存在这样的参数。

但是,我遇到了两个问题:

  1. 如何在换行符上拆分文本?(尝试过\readlist*\setsepchar{\n}没有成功 - 如何引用乳胶中的换行符?)
  2. 我如何生成表格?(\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

? 

在此处输入图片描述

相关内容