使用另一个 key=value 包

使用另一个 key=value 包

为了更好地理解,kvoptions我有以下最小示例包和文档。在包中,我根据三个布尔选项的逻辑状态构建了一个新的宏命令。布尔 false 设置的存在似乎不是问题,但我的问题是,考虑到我没有收到任何错误或警告,这是否是一种可以接受的操纵选项的方式。

在这种情况下,这三个选项是互斥的,这似乎是确保这一点的唯一方法。这只是从更大的上下文中获取的一个最小示例,其中已经存在一个用于在文档中随机更改设置的命令。没有人可以从这个 MWE 中知道这一点(缺乏上下文是使用 MWE IHMO 的危险,如此处所示)。我已经计划记录仅指定一个选项的要求(同样,从这个 MWE 中无法得知)。我使用了一个setup命令,因为我反复被告知那是“最好的方法”。因此,在正确运行且没有错误或警告的情况下,我怎么知道不应该做某事?如果有更好的方法来处理布尔值(我同意它在这里看起来有些尴尬),我愿意学习它。这就是我问这个问题的原因。

MWE 包:

\ProvidesPackage{tiny}[2021-01-05 v1.0 A tiny demo package]
\RequirePackage{kvoptions}
\SetupKeyvalOptions{%
  family=tiny,%
  prefix=tiny@,%
  setkeys=\kvsetkeys%
}%

\DeclareBoolOption[false]{optionone}
\DeclareBoolOption[false]{optiontwo}
\DeclareBoolOption[true]{optionthree}
\ProcessKeyvalOptions{tiny}

\newcommand*{\tinysetup}{%
  \kvsetkeys{tiny}%
}%

% This block doesn't quite work as expected with \tinysetup{}
%\AtBeginDocument{%
%  \iftiny@optionone
%    \newcommand*{\momentum}[1]{#1\,\mathrm{kg}\cdot\mathrm{m}\cdot\mathrm{s}^{-1}}
%    optionone is active\par
%    \tiny@optiontwofalse
%    optiontwo is not active\par
%    \tiny@optionthreefalse
%    optionthree is not active\par
%  \fi
%  \iftiny@optiontwo
%    \newcommand*{\momentum}[1]{#1\,\mathrm{kg}\cdot\mathrm{m}/\mathrm{s}}
%    \tiny@optiononefalse
%    optionone is not active\par
%    optiontwo is active\par
%    \tiny@optionthreefalse
%    optionthree is not active\par
%  \fi  
%  \iftiny@optionthree
%    \newcommand*{\momentum}[1]{#1\,\mathrm{N}\cdot\mathrm{s}}
%    \tiny@optiononefalse
%    optionone is not active\par
%    \tiny@optiontwofalse
%    optiontwo is not active\par
%    optionthree is active\par
%  \fi
%}%

% This block seems to work as expected with \tinysetup{}
\AtBeginDocument{%
  \newcommand*{\momentum}[1]{#1\,%
    \iftiny@optionone
      \tiny@optiontwofalse
      \tiny@optionthreefalse
      \mathrm{kg}\cdot\mathrm{m}\cdot\mathrm{s}^{-1}%
    \fi
    \iftiny@optiontwo
      \tiny@optiononefalse
      \tiny@optionthreefalse
      \mathrm{kg}\cdot\mathrm{m}/\mathrm{s}%
    \fi
    \iftiny@optionthree
      \tiny@optiononefalse
      \tiny@optiontwofalse
      \mathrm{N}\cdot\mathrm{s}%
    \fi
  }% % end of \newcommand
}%

这是使用该包的 MWE 文档:

% !TEX TS-program = lualatexmk
% !TEX encoding = UTF-8 Unicode

\documentclass{article}
\usepackage{tiny}

\begin{document}
Hello.

% should invoke optionthree
\( \momentum{3} \)

% should invoke optionone
\tinysetup{optionthree=false,optionone}
\( \momentum{3} \)

% should invoke optiontwo
\tinysetup{optionone=false,optiontwo}
\( \momentum{3} \)

% should again invoke optionthree
\tinysetup{optiontwo=false,optionthree}
\( \momentum{3} \)

\end{document}

答案1

使用另一个 key=value 包

编辑:我已经改变了这个代码示例以使用\chardef变体(请参阅下面的简短解释)。

我会放弃kvoptions并使用另一个 key=value 实现,它为此提供了类似键的选择。可行的选项将是l3keys(带有l3keys2e用于包选项支持的)、pgfkeys(带有pgfopts用于包选项支持的)、expkv(带有expkv-opt用于包选项支持的以及expkv-def用于预定义键类型的)或options

由于我是的作者expkv,下面是使用它的一个示例实现。

包裹:

\ProvidesPackage{tiny}[2021-01-05 v1.0 A tiny demo package]

\RequirePackage{expkv-opt,expkv-def}

\ekvdefinekeys{tiny}
  {
    protected choice option =
      {
         one   = {\chardef\tiny@option0 }
        ,two   = {\chardef\tiny@option1 }
        ,three = {\chardef\tiny@option2 }
      }
    ,initial option = three
  }

\ekvoProcessGlobalOptions{tiny}
\ekvoProcessLocalOptions{tiny}

\ekvsetdef\tinysetup{tiny}

% This block seems to work as expected with \tinysetup{}
\AtBeginDocument{%
  \newcommand*{\momentum}[1]{#1\,%
    \ifcase\tiny@option
      \mathrm{kg}\cdot\mathrm{m}\cdot\mathrm{s}^{-1}%
    \or
      \mathrm{kg}\cdot\mathrm{m}/\mathrm{s}%
    \or
      \mathrm{N}\cdot\mathrm{s}%
    \fi
  }% % end of \newcommand
}

文档:

\documentclass{article}
\usepackage{tiny}

\begin{document}
Hello.

% should invoke optionthree
\( \momentum{3} \)

% should invoke optionone
\tinysetup{option=one}
\( \momentum{3} \)

% should invoke optiontwo
\tinysetup{option=two}
\( \momentum{3} \)

% should again invoke optionthree
\tinysetup{option=three}
\( \momentum{3} \)

\end{document}

坚持kvoptions

如果您想坚持下去,kvoptions我会放弃Bool选项并使用三个Void直接设置布尔值的选项(类似于上面代码中的三个选择)。这样,这些键就不会采用任何值,但您会得到互斥选项的正确行为。

包裹:

\ProvidesPackage{tiny}[2021-01-05 v1.0 A tiny demo package]

\RequirePackage{kvoptions}
\SetupKeyvalOptions{%
  family=tiny,%
  prefix=tiny@,%
  setkeys=\kvsetkeys
}

\newif\iftiny@one
\newif\iftiny@two
\newif\iftiny@three
\tiny@threetrue

\DeclareVoidOption{optionone}  {\tiny@onetrue\tiny@twofalse\tiny@threefalse}
\DeclareVoidOption{optiontwo}  {\tiny@onefalse\tiny@twotrue\tiny@threefalse}
\DeclareVoidOption{optionthree}{\tiny@onefalse\tiny@twofalse\tiny@threetrue}

\ProcessKeyvalOptions{tiny}

\newcommand*\tinysetup{\kvsetkeys{tiny}}

% This block seems to work as expected with \tinysetup{}
\AtBeginDocument{%
  \newcommand*{\momentum}[1]{#1\,%
    \iftiny@one
      \mathrm{kg}\cdot\mathrm{m}\cdot\mathrm{s}^{-1}%
    \fi
    \iftiny@two
      \mathrm{kg}\cdot\mathrm{m}/\mathrm{s}%
    \fi
    \iftiny@three
      \mathrm{N}\cdot\mathrm{s}%
    \fi
  }% % end of \newcommand
}%

文档:

\documentclass{article}
\usepackage{tiny}

\begin{document}
Hello.

% should invoke optionthree
\( \momentum{3} \)

% should invoke optionone
\tinysetup{optionone}
\( \momentum{3} \)

% should invoke optiontwo
\tinysetup{optiontwo}
\( \momentum{3} \)

% should again invoke optionthree
\tinysetup{optionthree}
\( \momentum{3} \)

\end{document}

替代实施方案

除了使用三个之外\newif,也许更优雅的实现是使用一个宏,将相应的选择存储为数字(代码使用kvoptions,该原理也适用于expkv示例或任何其他 key=value 包):

包裹:

\ProvidesPackage{tiny}[2021-01-05 v1.0 A tiny demo package]

\RequirePackage{kvoptions}
\SetupKeyvalOptions{%
  family=tiny,%
  prefix=tiny@,%
  setkeys=\kvsetkeys
}

% initial value
\chardef\tiny@option2

\DeclareVoidOption{optionone}  {\chardef\tiny@option0 }
\DeclareVoidOption{optiontwo}  {\chardef\tiny@option1 }
\DeclareVoidOption{optionthree}{\chardef\tiny@option2 }

\ProcessKeyvalOptions{tiny}

\newcommand*\tinysetup{\kvsetkeys{tiny}}

% This block seems to work as expected with \tinysetup{}
\AtBeginDocument{%
  \newcommand*{\momentum}[1]{#1\,%
    \ifcase\tiny@option
      \mathrm{kg}\cdot\mathrm{m}\cdot\mathrm{s}^{-1}%
    \or
      \mathrm{kg}\cdot\mathrm{m}/\mathrm{s}%
    \or
      \mathrm{N}\cdot\mathrm{s}%
    \fi
  }% % end of \newcommand
}

文档:

\documentclass{article}
\usepackage{tiny}

\begin{document}
Hello.

% should invoke optionthree
\( \momentum{3} \)

% should invoke optionone
\tinysetup{optionone}
\( \momentum{3} \)

% should invoke optiontwo
\tinysetup{optiontwo}
\( \momentum{3} \)

% should again invoke optionthree
\tinysetup{optionthree}
\( \momentum{3} \)

\end{document}

答案2

这个答案只是为了好玩(因为 Skillmon 没有使用expl3:D 放东西)。正如您在问题描述中所说,有时,如果没有较长的 MWE,很难理解上下文,无论如何,既然expl3已经习惯了,最好尝试习惯它 :),这是我最好的尝试。

\documentclass{article}
\begin{filecontents*}[overwrite]{tiny.sty}
\RequirePackage{l3keys2e}
\ProvidesExplPackage
    {tiny}
    {2021-01-05}
    {1.0}
    {A tiny demo package}

% Define a bool vars
\bool_new:N \l_tiny_option_one_bool
\bool_new:N \l_tiny_option_two_bool
\bool_new:N \l_tiny_option_three_bool

% Define a keys
\keys_define:nn { tiny }
  {
    option .choice:,
    option / one   .code:n    = \bool_set_true:N \l_tiny_option_one_bool
                                \bool_set_false:N \l_tiny_option_two_bool
                                \bool_set_false:N \l_tiny_option_three_bool,
    option / two   .code:n    = \bool_set_true:N \l_tiny_option_two_bool
                                \bool_set_false:N \l_tiny_option_one_bool
                                \bool_set_false:N \l_tiny_option_three_bool,
    option / three .code:n    = \bool_set_true:N \l_tiny_option_three_bool
                                \bool_set_false:N \l_tiny_option_one_bool
                                \bool_set_false:N \l_tiny_option_two_bool,
    option         .initial:n = three,
    option .value_required:n  = true,
  }

% Process
\ProcessKeysOptions { tiny }

% Setup
\NewDocumentCommand\tinysetup{ m }
  {
    \keys_set:nn { tiny } { #1 }
  }

% Command
\AtBeginDocument{
  \NewDocumentCommand\momentum{ m }
    {
      \group_begin:
        \bool_if:NT \l_tiny_option_one_bool
          {
            #1\,\mathrm{kg}\cdot\mathrm{m}\cdot\mathrm{s}^{-1}
          }
        \bool_if:NT \l_tiny_option_two_bool
          {
            #1\,\mathrm{kg}\cdot\mathrm{m}/\mathrm{s}
          }
        \bool_if:NT \l_tiny_option_three_bool
          {
            #1\,\mathrm{N}\cdot\mathrm{s}
          }
      \group_end:
    }
}
\end{filecontents*}
\usepackage{tiny} % default option=three
%\usepackage[option=one]{tiny} % load option=one
\begin{document}

Hello.

% should invoke option=three
\( \momentum{3} \)

% should invoke option=one
\tinysetup{option=one}
\( \momentum{3} \)

% should invoke option=two
\tinysetup{option=two}
\( \momentum{3} \)

% should again invoke option=three
\tinysetup{option=three}
\( \momentum{3} \)

\end{document}

相关内容