为了更好地理解,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}