我想定义两个宏,它们一起生成一张发票表。
- 将条目添加到序列。不生成任何输出。
% \addentry{description}{quantity}{price}
\addentry{Item 1}{5}{4.50}
\addentry{Item 2}{3}{12.00}
- 将所有条目打印为表格,即
\printentries
应扩展为
\begin{tabularx}{\textwidth}{Xrrr}
\toprule
Description & Quantity & Price & Amount \\
\midrule
Item 1 & 5 & 4.50 & 22.50 \\
Item 2 & 3 & 12.00 & 36.00 \\
\bottomrule
\end{tabularx}
我迄今为止尝试过
\documentclass{article}
\usepackage{tabularx, booktabs}
\ExplSyntaxOn
\cs_generate_variant:Nn \seq_put_right:Nn {Ne}
\seq_new:N \l__entries_seq
\seq_new:N \l__formatted_entries_seq
\fp_new:N \l__entry_amount_fp
\prop_new:N \l__entry_prop
% \addentry{description}{quantity}{price}
\NewDocumentCommand\addentry{m m m}{
% Calculate the total amount of this entry by multiplying quantity with price
\fp_set:Nn \l__entry_amount_fp {#2 * #3}
% Build the entry
\prop_set_from_keyval:Nn \l__entry_prop {
description={#1},
quantity={#2},
price={#3},
amount={\fp_eval:n \l__entry_amount_fp}
}
% Add the entry to the sequence
\seq_put_right:Ne \l__entries_seq{
\prop_to_keyval:N \l__entry_prop % Serialize the property list representing the entry
}
}
\NewDocumentCommand\printentries{}{
% Map entries to their row representation (i.e., seperated by `&`) and temporarily store in `\l__formatted_entries_seq`
\seq_set_map:NNn \l__formatted_entries_seq \l__entries_seq {
\prop_set_from_keyval:Nn \l__entry_prop {##1} % Deserialize the property list representing the entry
\prop_item:Nn \l__entry_prop {description} &
\prop_item:Nn \l__entry_prop {quantity} &
\prop_item:Nn \l__entry_prop {price} &
\prop_item:Nn \l__entry_prop {amount}
}
% Generate the table
\begin{tabularx}{\textwidth}{Xrrr}
\toprule
Description & Quantity & Price & Amount \\
\midrule
\seq_use:Nn \l__formatted_entries_seq { \\ } \\
\bottomrule
\end{tabularx}
}
\ExplSyntaxOff
\addentry{Item 1}{5}{4.50}
\addentry{Item 2}{3}{12.00}
\begin{document}
\printentries
\end{document}
奇怪的是,这给了我以下输出。“Description”似乎正确,但“Item 1”的其他列不正确。为什么这是错误的?
使用 2021 或更早版本的 Latex 用户须知
您可能需要将此添加到您的前言中,以便我的代码能够编译。将其添加到 行之后\ExplSyntaxOn
。
%%%% POLYFILL OF THE `\prop_to_keyval:N` MACRO FOR OLD TEX VERSION %%%%
\makeatletter
\cs_new:Npn \prop_to_keyval:N #1
{
\__kernel_exp_not:w
\prop_if_empty:NTF #1
{ {} }
{
\exp_after:wN \exp_after:wN \exp_after:wN
{
\tex_expanded:D
{
\__kernel_exp_not:w { \use_none:n }
\prop_map_function:NN #1 \@@_to_keyval:nn
}
}
}
}
\cs_new:Npn \@@_to_keyval:nn #1#2
{ , ~ {#1} =~ { \__kernel_exp_not:w {#2} } }
\makeatother
%%%% END POLYFILL %%%%
答案1
我认为您不需要填充属性列表。只需逐步构建表主体即可。
\documentclass{article}
\usepackage{tabularx,booktabs}
\ExplSyntaxOn
\tl_new:N \g__safron_invoice_body_tl
\fp_new:N \g__safron_invoice_total_fp
\NewDocumentCommand{\addentry}{mmm}
{
\tl_gput_right:Nx \g__safron_invoice_body_tl
{
\exp_not:n { #1 } & #2 & #3 & \__safron_invoice_amount:e { \fp_eval:n { #2 * #3 } }
\exp_not:N \\
}
\fp_gadd:Nn \g__safron_invoice_total_fp { #2 * #3 }
}
\NewDocumentCommand{\printentries}{}
{
\noindent
\begin{tabularx}{\textwidth}{Xrrr}
\toprule
Description & Quantity & Price & Amount \\
\midrule
\tl_use:N \g__safron_invoice_body_tl
\midrule
Total &&& \__safron_invoice_amount:e { \fp_use:N \g__safron_invoice_total_fp } \\
\bottomrule
\end{tabularx}
}
\cs_new:Nn \__safron_invoice_amount:n
{
\__safron_invoice_amount:w #1 .. \q_stop
}
\cs_new:Npn \__safron_invoice_amount:w #1 . #2 . #3 \q_stop
{
\tl_if_empty:nTF { #2 }
{ #1.00 }
{ #1.#2 \prg_replicate:nn { 2 - \tl_count:n { #2 } } { 0 } }
}
\cs_generate_variant:Nn \__safron_invoice_amount:n { e }
\ExplSyntaxOff
\begin{document}
\addentry{Item 1}{5}{4.50}
\addentry{Item 2}{3}{12.00}
\printentries
\end{document}
答案2
egreg 已经提供了一个很棒的、可能更简单的解决方案。然而,当我试图更好地理解 expl3 时,我想知道如何调整你原来的方法,以便它输出正确的内容。
我认为,问题在于,在将事物作为键值实体存储在序列中之前,您需要对其进行扩展。例如,您需要扩展\fp_eval:n \l__entry_amount_fp
,否则您将把这个宏序列存储在序列中,并且它只会在打印时扩展,并且始终输出最后一个值。(您可以使用\seq_show:N
它将序列中的条目打印到日志中来检查这些内容。)
同样,您需要先扩展格式化的条目,然后再将它们添加到\l__formatted_entries_seq
序列中。一种非常可靠的方法是使用\seq_put_right:Nx
。您已经在宏的原始定义中使用了它的变体\addentry
。
\documentclass{article}
\usepackage{tabularx, booktabs}
\ExplSyntaxOn
\seq_new:N \l__entries_seq
\seq_new:N \l__formatted_entries_seq
\fp_new:N \l__entry_amount_fp
\prop_new:N \l__entry_prop
% \addentry{description}{quantity}{price}
\NewDocumentCommand\addentry{m m m}{
% Calculate the total amount of this entry by multiplying quantity with price
\fp_set:Nn \l__entry_amount_fp {#2 * #3}
% Build the entry
\prop_put:Nnx \l__entry_prop { description } {#1}
\prop_put:Nnx \l__entry_prop { quantity } {#2}
\prop_put:Nnx \l__entry_prop { price } {#3}
\prop_put:Nnx \l__entry_prop { amount } { \fp_eval:n \l__entry_amount_fp } % <-- !
% Add the entry to the sequence
\seq_put_right:Nx \l__entries_seq {
\prop_to_keyval:N \l__entry_prop % Serialize the property list representing the entry
}
}
\NewDocumentCommand\printentries{}{
% Map entries to their row representation (i.e., seperated by `&`) and temporarily store in `\l__formatted_entries_seq`
\seq_map_inline:Nn \l__entries_seq {
\prop_set_from_keyval:Nn \l__entry_prop {##1} % Deserialize the property list representing the entry
\seq_put_right:Nx \l__formatted_entries_seq { % <-- !
\prop_item:Nn \l__entry_prop {description} &
\prop_item:Nn \l__entry_prop {quantity} &
\prop_item:Nn \l__entry_prop {price} &
\prop_item:Nn \l__entry_prop {amount}
}
}
% Generate the table
\begin{tabularx}{\textwidth}{Xrrr}
\toprule
Description & Quantity & Price & Amount \\
\midrule
\seq_use:Nn \l__formatted_entries_seq { \\ } \\
\bottomrule
\end{tabularx}
}
\ExplSyntaxOff
\addentry{Item 1}{5}{4.50}
\addentry{Item 2}{3}{12.00}
\begin{document}
\printentries
\end{document}
当然,输出的格式不如 egreg 的答案那么好......