帮助制作更快的令牌替换宏

帮助制作更快的令牌替换宏

我有一个名为 的宏\logic,它通过简化一些常见的逻辑符号来帮助我更快地编写逻辑公式。因​​此,当我写入时,$\logic{p -> q}$我得到了$p \to q$

代码如下:

\documentclass{article}

\makeatletter
%
% Command to format a formula with a succinct and natural syntax
%
\usepackage{xparse}
\usepackage{xstring}% for \StrSubstitute
\usepackage{pgffor}% for \foreach

% the strange `\iffalse{\fi` thing is a 'brace hack', to make this work inside
% `tabular`-like envs. See https://tex.stackexchange.com/questions/452442
\NewDocumentCommand\logic{+m}{\iffalse{\fi
  \def\gt@formula{#1}%
  \saveexpandmode
  \saveexploremode
  \expandarg
  \exploregroups
  \foreach \gt@keyword/\gt@operator in \gt@logic@operators {%
    \StrSubstitute{\gt@formula}{\gt@keyword}{\gt@operator}[\@temp]%
    \global\let\gt@formula\@temp
  }%
  \restoreexploremode
  \restoreexpandmode
  \ensuremath{\gt@formula}%
\iffalse}\fi}

\def\gt@logic@operators{}

\NewDocumentCommand\RegisterLogicOperators{m}{
  \def\gt@logic@operators{#1}
}

\makeatother

\RegisterLogicOperators{
  !/\neg,
  &&/\land,
  &/\land,
  \&/\land,
  ||/\lor,
  |/\lor,
  <->/\longleftrigtharrow,
  ->/\to,
  ~>/\leadsto,
  <-/\impliedby,
  <</\llangle,
  >>/\rrangle,
  <=/\le,
  >=/\ge,
  ==/\equiv,
  [[/\llbracket,
  ]]/\rrbracket,
}


\begin{document}

This is a logic formula: $\logic{((p -> q) & p) -> q}$.


\end{document}

但是,这个宏非常慢。你用过一次后不会注意到,但在一篇使用率很高的论文中,编译时间会很快增加。我想这里的瓶颈是\StrSubstitute来自xstring包的宏,对于我要做的事情来说,这似乎有点过头了,但我不知道如何更快地获得相同的结果。还请注意,实际使用中对的调用有更长的替换列表\RegisterLogicOperators

有没有什么办法可以提高这个宏的性能?

答案1

您可以在 LaTeX3 中尝试此解决方案,它可以一次性转换整个字符串。我尝试一次转换 1000 个表达式,用时不到 5 秒。

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{amsmath, amssymb}
\usepackage{expl3}

\ExplSyntaxOn
\prop_new:N \g_symbol_conv_prop
\prop_gset_from_keyval:Nn \g_symbol_conv_prop {
  !=\neg,
  &&=\land,
  &=\land,
  \&=\land,
  ||=\lor,
  |=\lor,
  <->=\longleftrightarrow,
  ->=\to,
  ~>=\leadsto,
  <-=\impliedby,
  <<=\llangle,
  >>=\rrangle,
  {<=}=\le,
  {>=}=\ge,
  {==}=\equiv,
  [[=\llbracket,
  ]]=\rrbracket
}

% convert keys to string
\prop_new:N \g_str_symbol_conv_prop
\prop_map_inline:Nn \g_symbol_conv_prop {
  \prop_put:Nxx \g_str_symbol_conv_prop {\tl_to_str:n {#1}} {\exp_not:n {#2}}
}

\cs_set:Npn \mult_level_lookup_name:n #1 {
  __g_symbol_lookup_\int_to_alph:n{#1}_seq
}

% find longest symbol
\int_new:N \g_longest_symbol_len_int
\int_set:Nn \g_longest_symbol_len_int {0}
\prop_map_inline:Nn \g_str_symbol_conv_prop {
  \int_set:Nn \l_tmpa_int {\str_count:n {#1}}
  \int_set:Nn \g_longest_symbol_len_int {
    \int_max:nn {\g_longest_symbol_len_int} {\l_tmpa_int}
  }
}

% create multi-level look up tables
\int_step_inline:nn {\g_longest_symbol_len_int} {
  \seq_new:c {\mult_level_lookup_name:n {#1}}
}

% fill multi-level lookup tables
\prop_map_inline:Nn \g_str_symbol_conv_prop {
  \str_set:Nn \l_tmpa_str {#1}
  \int_step_inline:nn {\str_count:N \l_tmpa_str} {
    \seq_push:cx {\mult_level_lookup_name:n {##1}} {
      \str_item:Nn \l_tmpa_str {##1}
    }
  }
}

\tl_new:N \l_logic_result_tl
\int_new:N \l_current_level_int
\str_new:N \l_current_logic_str
\str_new:N \l_current_symbol_str

\msg_new:nnn {logic} {invalidsymbol} {"#1" is not a valid symbol}
\cs_generate_variant:Nn \tl_rescan:nn {nV}


\cs_set:Npn \logic_conv:n #1 {
  \str_set:Nn \l_current_logic_str {#1}
  \int_set:Nn \l_current_level_int {1}
  \tl_clear:N \l_logic_result_tl
  \str_clear:N \l_current_symbol_str
  
  \bool_do_until:nn {\str_if_empty_p:N \l_current_logic_str} {
    % pop first item
    \str_set:Nx \l_tmpa_str {\str_head:N \l_current_logic_str}
    \str_set:Nx \l_current_logic_str {\str_tail:N \l_current_logic_str}
    
    % check if this item can be found in the look up table
    \seq_if_in:cVTF {\mult_level_lookup_name:n {\l_current_level_int}} \l_tmpa_str {
      % if it can be found, advance level and store it into \l_current_symbol_str
      \int_incr:N \l_current_level_int
      \str_put_right:NV \l_current_symbol_str \l_tmpa_str
    }
    {
      % if it cannot be found, it could be the end of a symbol
      % or just a normal string
      \str_if_empty:NF \l_current_symbol_str {
        % if \l_current_symbol_str is not empty, it is the end of a symbol
        % we need to insert the correct symbol and reset variables
        \prop_get:NVNTF \g_str_symbol_conv_prop \l_current_symbol_str \l_tmpa_tl {
          \int_set:Nn \l_current_level_int {1}
          \str_clear:N \l_current_symbol_str
          \tl_put_right:NV \l_logic_result_tl \l_tmpa_tl
        } {
          \tl_put_right:NV \l_logic_result_tl \l_current_symbol_str
          \int_set:Nn \l_current_level_int {1}
          \str_clear:N \l_current_symbol_str
        }
      }
      \tl_put_right:NV \l_logic_result_tl \l_tmpa_str
    }
  }
  % output last item
  \prop_get:NVNTF \g_str_symbol_conv_prop \l_current_symbol_str \l_tmpa_tl {
    \tl_put_right:NV \l_logic_result_tl \l_tmpa_tl
  } { \tl_put_right:NV \l_logic_result_tl \l_current_symbol_str }
  
  %\tl_show:N \l_logic_result_tl
  \tl_rescan:nV {} \l_logic_result_tl
}

\newcommand{\logic}[1]{
  \logic_conv:n {#1}
}


\ExplSyntaxOff

\begin{document}

$\logic{((p -> q) & p | !r) <= >= == -> <- c | d \phi || \sigma}$

\end{document}

相关内容