如何在 LaTeX3 中比较不同的数据类型

如何在 LaTeX3 中比较不同的数据类型

我正在尝试对值进行基本的比较,但不确定如何正确地进行(仅使用 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

以下只是一个建议。我认为这永远不应该被使用,但它展示了如何对输入的类型进行一些检查并定义某种类型的变量。

它为类型tlstrint和实现了此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数据结构以无法输入文档的形式构建,因此不能按照要求的方式简单地测试它们。

最有可能的是,有更好的方法可以解决当前的问题,但它们取决于具体的用例。

相关内容