处理具有许多非预定义键的键/值选项

处理具有许多非预定义键的键/值选项

假设我想定义一个命令\defineaspect,该命令采用键/值选项列表来将值与事先未知的键关联起来。例如,要将某些方面与人员列表关联起来,该命令的使用可能看起来像

\defineaspect{forename}{
    .print = {},
    sherlock = Sherlock,
    jim = James
}
\defineaspect{surname}{
    .print = \scshape,
    sherlock = Holmes,
    jim = Moriarty
}

sherlock然后jim应将其视为数据库表中的主键,各个方面是该表中的进一步列。此外,一些实际预定义的键也可能出现在此列表中,如.print示例中所示。

我浏览了一些用于键/值处理的包,但据我所知,拥有大量非预定义键并不是这些包的典型用例。

是否有一个键/值包可以轻松处理此类键?或者我最好使用自定义解析器,先拆分列表,,然后分别解析每个键/值对?

答案1

您可以利用expl3的键值系统和prop数据类型。要将键值列表转换为expl3属性列表,您可以使用\prop_set_from_keyval:Nn。该函数将获取key = value中的每个对#2并添加到prop中的列表中#1。本例中的示例文档如下:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \defineaspect { mm }
  {
    \prop_new:c { g__siracusa_#1_aspect_prop }
    \prop_gset_from_keyval:cn { g__siracusa_#1_aspect_prop } {#2}
  }
%
\tl_new:N \l__siracusa_tmpa_tl
\NewDocumentCommand \getaspect { mm }
  {
    \prop_get:cnNTF { g__siracusa_#1_aspect_prop } {#2} \l__siracusa_tmpa_tl
      { \tl_use:N \l__siracusa_tmpa_tl }
      { Aspect~#2~does~not~exist~in~#1! }
  }
\NewDocumentCommand \fullname { m }
  {
    { \getaspect { forename } { .print } \getaspect { forename } {#1} ~ }
    { \getaspect { surname  } { .print } \getaspect { surname  } {#1} }
  }
\ExplSyntaxOff
\defineaspect{forename}{
    .print = {},
    sherlock = Sherlock,
    jim = James
}
\defineaspect{surname}{
    .print = \scshape,
    sherlock = Holmes,
    jim = Moriarty
}
\begin{document}
Did you miss me? --- \fullname{jim}
\end{document}

但是,这会将输入一对一地转换为数据结构。如果您需要更精细地控制该过程,例如,区分简单键、键和“特殊”键,例如.print,那么您可以使用\keyval_parse:NNn

\keyval_parse:NNn接受两个函数和一个键值列表作为参数。\keyval_parse:NNn遍历键值列表,#3并对列表的每个项目使用函数#1或,#2具体取决于项目是否只有一个键或一个键和一个值。

上述内容的重新实施\defineaspect将变为:

\prop_new:N \l__siracusa_tmpa_prop
\NewDocumentCommand \defineaspect { mm }
  {
    \prop_clear:N \l__siracusa_tmpa_prop
    \keyval_parse:NNn \__siracusa_aspect:n \__siracusa_aspect:nn {#2}
    \prop_new:c { g__siracusa_#1_aspect_prop }
    \prop_gset_eq:cN { g__siracusa_#1_aspect_prop } \l__siracusa_tmpa_prop
  }
\cs_new_protected:Npn \__siracusa_aspect:n #1
  { \__kernel_msg_error:nnn { kernel } { prop-keyval } {#1} }
\cs_new_protected:Npn \__siracusa_aspect:nn #1#2
  { \prop_put:Nnn \l__siracusa_tmpa_prop {#1} {#2} }

定义中的错误消息\__siracusa_aspect:n只是为了模仿的行为\prop_set_from_keyval:Nn,但当然,当仅给出一个键时,您可以将其更改为适当的操作。

相关内容