我正在尝试对值进行基本的比较,但不确定如何正确地进行(仅使用 LaTeX3 功能,因此没有xifthen
)。
从文档来看,这些是唯一通用的 if 函数:
\if:w
\if_bool:N
\if_box_empty:N
\if_case:w
\if_catcode:w
\if_charcode:w
\if_cs_exist:N
\if_cs_exist:w
\if_dim:w
\if_eof:w
\if_false:
\if_hbox:N
\if_int_compare:w
\if_int_odd:w
\if_meaning:w
\if_mode_horizontal:
\if_mode_inner:
\if_mode_math:
\if_mode_vertical:
\if_predicate:w
\if_true:
\if_vbox:N
还有这个:
\IfValueTF
检查参数是否存在。
我想知道如何将任意值类型相互比较。这样做的原因是输入值可能是任何值,并且应该可以根据感兴趣的特定值进行测试。
例如,我希望能够做到这一点:
\ifeq \something 10 % integer
\ifeq \something true % boolean
\ifeq \something 10.12 % float
\ifeq \something foo=bar % prop list
\ifeq \something [1,2,3] % seq
其中\something
是相同的任意变量,因此最终本质上是:
\ifeq 5 10
\ifeq 5 true
\ifeq 5 10.12
\ifeq 5 foo=bar
\ifeq 5 [1,2,3]
% or
\ifeq foo 10
\ifeq foo true
\ifeq foo 10.12
\ifeq foo foo=bar
\ifeq foo [1,2,3]
% or
\ifeq a=b 10
\ifeq a=b true
\ifeq a=b 10.12
\ifeq a=b foo=bar
\ifeq a=b [1,2,3]
% etc.
想知道如何进行这种类型检查。到目前为止,我得到的 MWE 是:
\documentclass[a4paper]{article}
\usepackage{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\gettype}{m}{
... return int % if int
... return clist % if clist
... return proplist % if prop list
... return seq % if seq
... return bool % if boolean
...
}
\NewDocumentCommand{\ifeq}{mmmO{}}{
% int case
\if \gettype{#1} == int {
\if \gettype{#2} != int {
\#4 % false branch
}
\else {
% comparison for each type perhaps, not sure
\if #1 == #2 {
\#3
}
}
}
% i'd handle all these cases somehow once i understand
% boolean case
% float case
% prop list case
% seq case
% clist case
% tl case
}
\begin{document}
\ifeq{5}{10}
\ifeq{5}{true}
\ifeq{5}{10.12}
\ifeq{5}{foo=bar}
\ifeq{5}{[1,2,3]}
% or
\ifeq{foo}{10}
\ifeq{foo}{true}
\ifeq{foo}{10.12}
\ifeq{foo}{foo=bar}
\ifeq{foo}{[1,2,3]}
% or
\ifeq{a=b}{10}
\ifeq{a=b}{true}
\ifeq{a=b}{10.12}
\ifeq{a=b}{foo=bar}
\ifeq{a=b}{[1,2,3]}
% etc.
\end{document}
答案1
以下只是一个建议。我认为这永远不应该被使用,但它展示了如何对输入的类型进行一些检查并定义某种类型的变量。
它为类型tl
、str
、int
和实现了此fp
功能。为了进一步扩展它,必须定义一个应与新数据类型匹配的正则表达式和一个必须添加到 的比较函数str_case:nn
。
\documentclass[]{article}
\usepackage{xparse}
\ExplSyntaxOn
\makeatletter
% regexes for comparison
\regex_const:Nn \c_lance_pollard_tl_regex { \A.*\Z }% could be anything
\regex_const:Nn \c_lance_pollard_str_regex { \A\c[OS].*\Z }% could be anything
\regex_const:Nn \c_lance_pollard_int_regex { \A[+\-]?\d+\Z }
\regex_const:Nn \c_lance_pollard_fp_regex { \A[+\-]?\d+\.?\d*\Z }
\NewDocumentCommand\newsomething{ m }
{
% #1 : type
\str_if_eq:nnTF { #1 } { str }
{ \newsomethingVerbatim { #1 } }
{ \newsomethingNormal { #1 } }
}
\NewDocumentCommand\newsomethingNormal { m m +m }
{
% #1 : type
% #2 : name
% #3 : value
\lance_pollard_new_something:nnn { #1 } { #2 } { #3 }
}
\NewDocumentCommand\newsomethingVerbatim { m m +v }
{
% #1 : type
% #2 : name
% #3 : value
\lance_pollard_new_something:nnn { #1 } { #2 } { #3 }
}
\cs_new:Nn \lance_pollard_new_something:nnn
{
% #1 : type
% #2 : name
% #3 : value
\cs_if_exist:cTF { c_lance_pollard_#1_regex }
{
\exp_args:Nc \regex_match:NnTF { c_lance_pollard_#1_regex } { #3 }
{
\tl_set:cn { l_lance_pollard_#2_type_tl } { #1 }
\use:c { #1_new:c } { l_lance_pollard_#2_val_#1 }
\use:c { #1_set:cn } { l_lance_pollard_#2_val_#1 } { #3 }
}
{
\msg_error:nnnnn { lance_pollard } { badly~formatted }
{ #3 } { #2 } { #1 }
}
}
{
\msg_error:nnn { lance_pollard } { type~not~setup } { #1 }
}
}
\NewDocumentCommand\ifsomethingeq { m }
{
% #1 : name
\exp_args:Nx \str_if_eq:nnTF
{ \tl_use:c { l_lance_pollard_#1_type_tl } }
{ str }
{ \ifsomethingeqVerbatim { #1 } }
{ \ifsomethingeqNormal { #1 } }
}
\NewDocumentCommand\ifsomethingeqNormal { m +m +m +m }
{
% #1 : name which was defined with \newsomething
% #2 : value to compare with
% #3 : T
% #4 : F
\lance_pollard_if_something_eq:nnnn { #1 } { #2 } { #3 } { #4 }
}
\NewDocumentCommand\ifsomethingeqVerbatim { m +v +m +m }
{
% #1 : name which was defined with \newsomething
% #2 : value to compare with
% #3 : T
% #4 : F
\lance_pollard_if_something_eq:nnnn { #1 } { #2 } { #3 } { #4 }
}
\cs_new:Nn \lance_pollard_if_something_eq:nnnn
{
% #1 : name which was defined with \newsomething
% #2 : value to compare with
% #3 : T
% #4 : F
\lance_pollard_if_something_eq:xnnTF
{ \tl_use:c { l_lance_pollard_#1_type_tl } } { #1 } { #2 }
{ #3 }
{ #4 }
}
\cs_new:Nn \lance_pollard_if_something_eq:nnnTF
{
% #1 : type
% #2 : name
% #3 : value to compare with
% #4 : T
% #5 : F
\lance_pollard_if_type:nnTF { #1 } { #3 }
{
\exp_args:Nnc
\lance_pollard_cmp_type:nNnTF { #1 }
{ l_lance_pollard_#2_val_#1 } { #3 }
{ #4 }
{ #5 }
}
{ #5 }
}
\cs_generate_variant:Nn \lance_pollard_if_something_eq:nnnTF { xnnTF }
\cs_new:Nn \lance_pollard_if_type:nnTF
{
% #1 : type
% #2 : value to compare with
% #3 : T
% #4 : F
\cs_if_exist:cTF { c_lance_pollard_#1_regex }
{
\exp_args:Nc \regex_match:NnTF { c_lance_pollard_#1_regex } { #2 }
{ #3 }
{ #4 }
}
{
\msg_error:nnn { lance_pollard } { type~not~setup } { #1 }
#4
}
}
\cs_new:Nn \lance_pollard_cmp_type:nNnTF
{
% #1 : type
% #2 : name of macro storing value
% #3 : value to compare with
% #4 : T
% #5 : F
\str_case:nn { #1 }
{
{ tl } { \exp_args:NV \tl_if_eq:nnTF #2 { #3 } { #4 } { #5 } }
{ int } { \lance_pollard_cmp_int:nnTF { #2 } { #3 } { #4 } { #5 } }
{ fp } { \lance_pollard_cmp_fp:nnTF { #2 } { #3 } { #4 } { #5 } }
{ str } { \str_if_eq:VnTF #2 { #3 } { #4 } { #5 } }
}
}
\cs_new:Nn \lance_pollard_cmp_int:nnTF
{
\int_compare:nNnTF { #1 } = { #2 }
{ #3 }
{ #4 }
}
\cs_new:Nn \lance_pollard_cmp_fp:nnTF
{
\fp_compare:nNnTF { #1 } = { #2 }
{ #3 }
{ #4 }
}
\msg_new:nnn { lance_pollard } { type~not~setup }
{
The~used~type~'#1'~was~not~setup~for~use~with~\newsomething
and~\ifsomethingeq.
}
\msg_new:nnn { lance_pollard } { badly~formatted }
{
The~initial~value~'#1'~for~'#2'~doesn't~match~the~defined~format~of~the~
type~'#3'.
}
\makeatother
\ExplSyntaxOff
\newsomething{fp}{myfp}{10.1}
\newsomething{int}{myint}{10}
\newsomething{tl}{mytl}{foobar}
\newsomething{str}{mystr}|$^_}{ern|
\begin{document}
\ifsomethingeq{myfp}{10.1}{equal}{not equal}
\ifsomethingeq{myint}{10}{equal}{not equal}
\ifsomethingeq{mytl}{foobar}{equal}{not equal}
\ifsomethingeq{mystr}|$^_}{ern|{equal}{not equal}
\ifsomethingeq{myfp}{foobar}{equal}{not equal}
\ifsomethingeq{myint}{foobar}{equal}{not equal}
\ifsomethingeq{mytl}{bazbang}{equal}{not equal}
\ifsomethingeq{mystr}{foobar}{equal}{not equal}
\end{document}
答案2
xparse
或中都没有内置“猜测数据格式”命令expl3
。一般来说,我们希望输入是定义明确的,而实际上对数据类型进行错误检查可能有些棘手。
归根结底,TeX 中的所有文字输入都是一个标记列表。所以我们总是可以测试它们是否完全相同:这是一个代码级函数
\tl_if_eq:nnTF { foo } {#1}
{ True code }
{ False code }
很多时候,你可能更愿意进行字符串测试
\str_if_eq:nnTF { foo } {#1}
{ True code }
{ False code }
其不同之处在于它与 catcode 无关。
除此之外,您还需要定义适当的检查函数。当然,问题是这些函数可能是什么。例如,+-+-\numexpr 1 0\relax
从 TeX 的角度来看,这是一个非常合理的数字。生活变得更加“有趣”,就像这样[1,2,3]
,它根本不是一个序列:它是一个逗号列表。几个expl3
数据结构以无法输入文档的形式构建,因此不能按照要求的方式简单地测试它们。
最有可能的是,有更好的方法可以解决当前的问题,但它们取决于具体的用例。