我想通过带有键/值的命令构建表格,值本身就是一组键/值(或列表)。
如果未定义键,则表格中不会添加任何条目。
目前,这是我的代码。
\documentclass{memoir}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { evaluation }
{
cc .tl_set:N = \l__evaluation_cc,
cc .initial:n = 0,
ds .tl_set:N = \l__evaluation_ds,
ds .initial:n = 0,
dm .tl_set:N = \l__evaluation_dm,
dm .initial:n = 0,
tp .tl_set:N = \l__evaluation_tp,
tp .initial:n = 0,
projet .tl_set:N = \l__evaluation_projet,
projet .initial:n = 0,
}
\NewDocumentCommand{\Evaluation}{O{}}
{
\group_begin:
\keys_set:nn {evaluation}{#1}
\begin{tabular}{*{4}{c}}
Type & Number & Exam & home work\\
CC & \\ % only if cc is a used key
DS & \\ % only if ds is a used key
$\vdots$\\
\end{tabular}
\group_end:
}
\ExplSyntaxOff
\begin{document}
\Evaluation[cc={n=1;e=2;h=3}, projet={h=8}]
\end{document}
- 如何管理两个级别的键/值(或任何其他方式来做到这一点)?
- 如何测试密钥是否已定义?
非常感谢!
答案1
使用问题中的模块,您可以使用来expl3
解析各个行。由于除了key=value 系统之外,系统无法完全扩展,因此我们必须为 key=value 解析构建一个临时文件。l3keys
prop
\keyval_parse:nnn
expl3
prop
我们可以通过设置一个特殊标记作为默认值来检查某个键是否被使用,然后可以在内部针对该标记进行测试\__nbur_evaluation_parse_row:nn
。
\documentclass{memoir}
\usepackage{xparse}
\ExplSyntaxOn
\keys_define:nn { evaluation }
{
cc .tl_set:N = \l__nbur_evaluation_cc_tl,
cc .initial:n = \q_no_value,
ds .tl_set:N = \l__nbur_evaluation_ds_tl,
ds .initial:n = \q_no_value,
project .tl_set:N = \l__nbur_evaluation_projet_tl,
project .initial:n = 0,
}
\prop_new:N \l__nbur_evaluation_row_prop
\cs_new_protected:Npn \__nbur_evaluation_parse_row:nn #1 #2
{
\quark_if_no_value:nF {#2}
{
\prop_set_from_keyval:Nn \l__nbur_evaluation_row_prop {#2}
\use:x
{
\exp_not:n {#1}
& \prop_item:Nn \l__nbur_evaluation_row_prop { n }
& \prop_item:Nn \l__nbur_evaluation_row_prop { e }
& \prop_item:Nn \l__nbur_evaluation_row_prop { h }
}
\\
}
}
\cs_generate_variant:Nn \__nbur_evaluation_parse_row:nn { nV }
\NewDocumentCommand{\Evaluation}{O{}}
{
\group_begin:
\keys_set_known:nn {evaluation}{#1}
\begin{tabular}{*{4}{c}}
Type & Number & Exam & home work\\
\__nbur_evaluation_parse_row:nV {CC} \l__nbur_evaluation_cc_tl
\__nbur_evaluation_parse_row:nV {DS} \l__nbur_evaluation_ds_tl
$\vdots$\\
\end{tabular}
\group_end:
}
\ExplSyntaxOff
\begin{document}
\Evaluation[cc={n=1,e=2,h=3,j=error}, projet={h=8}]
\end{document}
答案2
如果您想将 key=value 列表拆分成表的行,则需要一组可能的第二级键,这些键对于所有顶级键都是通用的。
下面使用expkv
和 这两个扩展expkv-def
来expkv-cs
实现这一点。顶层是一个用 定义的典型 key=value 接口\ekvdefinekeys
。keydata
将存储值并同时充当布尔开关。如果使用了 key,则定义的宏将扩展为其第一个参数,后跟提供给 key 的值。如果没有使用 key,则定义的宏将仅扩展为其第二个参数。我们可以使用它来为解析第二层的宏提供值,\evaluation@row
在本例中称为第二层。
\evaluation@row
定义为\ekvcSplit
,它将接受键n
、e
和h
(所有键的默认值均为空),并扩展为#1
,#1
即上一个列表中定义的第一个键,#2
然后是第二个键,依此类推。使此方法适用于表格单元格的巧妙之处在于expkv-cs
的 key=value 接口仍然完全可扩展。
\documentclass{memoir}
\usepackage{xparse}
\usepackage{expkv-cs,expkv-def}
\makeatletter
% define the top level keys
\ekvdefinekeys{evaluation}
{%
data cc = \evaluation@cc,
data ds = \evaluation@ds,
store project = \evaluation@project
}
\ekvsetdef\evaluation@kv{evaluation}
% define the keys at second level and split them into $n$ & $e$ & $h$
\ekvcSplit\evaluation@row{n=,e=,h=}{#1}
\NewDocumentCommand\Evaluation{O{}}
{%
\begingroup
\evaluation@kv{#1}%
\begin{tabular}{*{4}{c}}
Type & Number & Exam & home work\\
CC & \evaluation@cc\evaluation@row{&&} \\
DS & \evaluation@ds\evaluation@row{&&} \\
$\vdots$\\
\end{tabular}
\endgroup
}
% silently ignore unknown keys in the top level
\ekvdefunknown{evaluation}{}
\ekvdefunknownNoVal{evaluation}{}
% silently ignore unknown keys in the second level
\ekvdefunknown{\string\evaluation@row}{}
\ekvdefunknownNoVal{\string\evaluation@row}{}
\makeatother
\begin{document}
\Evaluation[cc={n=1,e=2,h=3,j=error}, projet={h=8}]
\end{document}
经过一些调整,我们可以使用 key 类型的可能性data
来省略此变体中未指定的行。必要的调整将是使用\ekvcSplitAndForward
而不是\ekvcSplit
,因为下一步必须获取一个附加参数(行的名称)。-key 定义宏的第二个参数data
必须是\@gobble
,这将仅删除行的名称。
\documentclass{memoir}
\usepackage{xparse}
\usepackage{expkv-cs,expkv-def}
\makeatletter
% define the top level keys
\ekvdefinekeys{evaluation}
{%
data cc = \evaluation@cc,
data ds = \evaluation@ds,
store project = \evaluation@project
}
\ekvsetdef\evaluation@kv{evaluation}
% define the keys at second level and split them into arguments for
% `\evaluation@row@`. You can specify default values for `n`, `e`, and `h` after
% the equals signs (currently empty).
\ekvcSplitAndForward\evaluation@row\evaluation@row@{n=,e=,h=}
\newcommand\evaluation@row@[4]{#4\\}
\NewDocumentCommand\Evaluation{O{}}
{%
\begingroup
\evaluation@kv{#1}%
\begin{tabular}{*{4}{c}}
Type & Number & Exam & home work\\
\evaluation@cc\evaluation@row\@gobble{CC}%
\evaluation@ds\evaluation@row\@gobble{DS}%
$\vdots$\\
\end{tabular}
\endgroup
}
% ignore unknown keys in the top level
\ekvdefunknown{evaluation}{}
\ekvdefunknownNoVal{evaluation}{}
% ignore unknown keys in the second level
\ekvdefunknown{\string\evaluation@row}{}
\ekvdefunknownNoVal{\string\evaluation@row}{}
\makeatother
\begin{document}
\Evaluation[cc={n=1,e=2,h=3,j=error}, projet={h=8}]
\end{document}
答案3
通过改变语法(没有分号),您可以评估键并将给定的值存储在属性列表中。
仅当主键已设置时,才会打印值,方法是检查相应属性列表是否为空。未出现的 n、e 或 h 值将被打印为 0。
\documentclass{memoir}
\usepackage{booktabs}
%\usepackage{xparse} % not needed for LaTeX 2020-10-01 or later
\ExplSyntaxOn
\prop_new:N \l__evaluation_cc_prop
\prop_new:N \l__evaluation_ds_prop
\prop_new:N \l__evaluation_dm_prop
\prop_new:N \l__evaluation_tp_prop
\prop_new:N \l__evaluation_projet_prop
\keys_define:nn { evaluation }
{
cc .code:n = \prop_set_from_keyval:Nn \l__evaluation_cc_prop { #1 },
ds .code:n = \prop_set_from_keyval:Nn \l__evaluation_ds_prop { #1 },
dm .code:n = \prop_set_from_keyval:Nn \l__evaluation_dm_prop { #1 },
tp .code:n = \prop_set_from_keyval:Nn \l__evaluation_tp_prop { #1 },
projet .code:n = \prop_set_from_keyval:Nn \l__evaluation_projet_prop { #1 },
}
\NewDocumentCommand{\Evaluation}{m}
{
\group_begin:
\keys_set:nn {evaluation}{#1}
\begin{tabular}{*{4}{c}}
\toprule
Type & Number & Exam & Homework\\
\midrule
\__evaluation_table_row:n { cc }
\__evaluation_table_row:n { ds }
\__evaluation_table_row:n { dm }
\__evaluation_table_row:n { tp }
\__evaluation_table_row:n { projet }
\bottomrule
\end{tabular}
\group_end:
}
\cs_new:Nn \__evaluation_table_row:n
{
\prop_if_empty:cF { l__evaluation_#1_prop }
{
\str_uppercase:n { #1 } &
\__evaluation_item:nn { #1 } { n } &
\__evaluation_item:nn { #1 } { e } &
\__evaluation_item:nn { #1 } { h } \\
}
}
\cs_new:Nn \__evaluation_item:nn
{
\prop_if_in:cnTF { l__evaluation_#1_prop } { #2 }
{ \prop_item:cn { l__evaluation_#1_prop } { #2 } }
{ 0 }
}
\ExplSyntaxOff
\begin{document}
\Evaluation{cc={n=1,e=2,h=3}, projet={h=8}}
\end{document}
该方法还可以用于不同的上下文中,对于更大的属性列表,可能会出现性能问题\prop_item:Nn
。根据 Skillmon 的建议(参见评论),这里是使用更快函数的替代实现\prop_get:NnNTF
。
\documentclass{memoir}
\usepackage{booktabs}
%\usepackage{xparse} % not needed for LaTeX 2020-10-01 or later
\ExplSyntaxOn
\prop_new:N \l__evaluation_cc_prop
\prop_new:N \l__evaluation_ds_prop
\prop_new:N \l__evaluation_dm_prop
\prop_new:N \l__evaluation_tp_prop
\prop_new:N \l__evaluation_projet_prop
\keys_define:nn { evaluation }
{
cc .code:n = \prop_set_from_keyval:Nn \l__evaluation_cc_prop { #1 },
ds .code:n = \prop_set_from_keyval:Nn \l__evaluation_ds_prop { #1 },
dm .code:n = \prop_set_from_keyval:Nn \l__evaluation_dm_prop { #1 },
tp .code:n = \prop_set_from_keyval:Nn \l__evaluation_tp_prop { #1 },
projet .code:n = \prop_set_from_keyval:Nn \l__evaluation_projet_prop { #1 },
}
\NewDocumentCommand{\Evaluation}{m}
{
\group_begin:
\keys_set:nn {evaluation}{#1}
\begin{tabular}{*{4}{c}}
\toprule
Type & Number & Exam & Homework\\
\midrule
\__evaluation_table_row:n { cc }
\__evaluation_table_row:n { ds }
\__evaluation_table_row:n { dm }
\__evaluation_table_row:n { tp }
\__evaluation_table_row:n { projet }
\bottomrule
\end{tabular}
\group_end:
}
\tl_new:N \l__evaluation_item_tl
\cs_new_protected:Nn \__evaluation_table_row:n
{
\prop_if_empty:cF { l__evaluation_#1_prop }
{
\str_uppercase:n { #1 } &
\__evaluation_item:nn { #1 } { n } &
\__evaluation_item:nn { #1 } { e } &
\__evaluation_item:nn { #1 } { h } \\
}
}
\cs_new_protected:Nn \__evaluation_item:nn
{
\prop_get:cnNTF { l__evaluation_#1_prop } { #2 } \l__evaluation_item_tl
{ \tl_use:N \l__evaluation_item_tl } % the key exists
{ 0 } % no such key stored
}
\ExplSyntaxOff
\begin{document}
\Evaluation{cc={n=1,e=2,h=3}, projet={h=8}}
\end{document}