尋求動態 LaTeX 命令替換的增強功能和解決方案

尋求動態 LaTeX 命令替換的增強功能和解決方案

我正在與社群合作,尋求 LaTeX 巨集客製化的見解和可能的解決方案。這是受到以下討論的啟發,它探索了用控制序列動態替換令牌的可能性。具體來說,線程中的第二個答案為我目前的方法提供了基礎,旨在使用命令用指定的控制序列替換任意符號或單字\myspecdef somethinghere : \somecontrolseq

以下是上述線程第二個答案的程式碼摘錄,演示了創建此功能的初步嘗試:

\long\def\isnextchar#1#2#3{\begingroup\toks0={\endgroup#2}\toks1={\endgroup#3}%
   \let\tmp=#1\futurelet\next\isnextcharA
}
\def\isnextcharA{\the\toks\ifx\tmp\next0\else1\fi\space}

\def\skipnext#1#2{#1}    
\def\trynext#1{\trynextA#1\relax\relax}
\def\trynextA#1#2\relax#3\relax#4#5{%
   \ifx\relax#2\relax \def\next{\isnextchar#1{\skipnext{#4}}{#5#3}}\else
      \def\next{\isnextchar#1{\skipnext{\trynextA#2\relax#3#1\relax#4{#5}}}{#5#3}}\fi
   \next
}
\def\mspecdefA#1#2#3 : #4{\ifx#2\undefined
   \def#2{\trynext{#3}#4{#1}}\else
   \toks0={\trynext{#3}#4}\toks1=\expandafter{#2}%
   \edef#2{\the\toks0{\the\toks1}}\fi
}
\def\mspecdef#1{%
   \expandafter\ifx\csname m:#1\endcsname\relax
      \expandafter\mathchardef\csname m:#1\endcsname=\mathcode`#1
   \fi
   \mathcode`#1="8000 
   \begingroup \lccode`~=`#1 
   \lowercase{\endgroup\expandafter\mspecdefA\csname m:#1\endcsname~}%
}
\mspecdef << : \ll
\mspecdef <> : \neq
\mspecdef <= : \leq
\mspecdef <== : \Leftarrow
\mspecdef <=> : \Leftrightarrow
\mspecdef <-- : \leftarrow
\mspecdef <-> : \leftrightarrow
\mspecdef >> : \gg
\mspecdef >= : \geq
\mspecdef --> : \rightarrow
\mspecdef -+ : \pm
\mspecdef +- : \mp
\mspecdef ... : \dots
\mspecdef == : \equiv
\mspecdef =. : \doteq
\mspecdef ==> : \Rightarrow
\mspecdef =( : \subseteq
\mspecdef =) : \supseteq
\mspecdef =[ : \sqsubseteq
\mspecdef =] : \sqsubseteq

\myspecdef integration : \int %<- an example of what I want
\myspecdef int : \int %<- this produces an error whilst...
\myspecdef int : \sin %<- does not!

test:

$$ a << b < c <= d >= e > f >> g $$
$$ a <> b = c =. d == e $$
$$ a <== b <-- c <-> d <=> e --> f ==> g $$
$$ a +- b = -(-a -+ +b) $$
$$ a, ..., z <> a + ...+ z $$
$$ a =( b =) c =[ e =] f $$

\[ x^+ \] %<- this produces an error

儘管此方法允許動態替換,例如轉換integral\int,但它引入了幾個問題。值得注意的是,該指令會導致某些輸入出現意外的大括號錯誤,例如\[0^+\]定義\myspecdef +- : \pm.雖然替代符號\[0^{+}\]可以解決這個問題,但這並不是一個理想的要求。此外,替換會無意中影響波浪號 ~ 字元的功能,導致出現諸如缺少數字、被視為零之類的錯誤。此外,由於定義中包含了,因此使用類似命令\operatorname會導致錯誤。'bad mathchar'-

為了解決這些問題並改進命令的行為,該線程的第四個答案提供了使用 expl3 語法的替代方法,如下所示:

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}

\ExplSyntaxOn

\seq_new:N \l_math_subs_seq

\cs_new_protected:Npn \math_add_sub:nn #1 #2
 {
   \seq_put_right:Nn \l_math_subs_seq { { #1 } { #2 } }
 }

\cs_new_protected:Npn \math_ascii_sub:n #1
 {
  \tl_set:Nn \l_tmpa_tl { #1 }
  \seq_map_inline:Nn \l_math_subs_seq
   {
    \tl_replace_all:Nnn \l_tmpa_tl ##1
   }
  \tl_use:N \l_tmpa_tl
 }

\cs_new_protected:Npn \math_grabinline:w #1 $
 {
  \math_ascii_sub:n { #1 } $
 }

\cs_new_protected:Npn \math_grabdisplay:w #1 \]
 {
  \math_ascii_sub:n { #1 } \]
 }

% Set substitutions (be careful with order!)
% Three letter sequences first
\math_add_sub:nn { <== } { \Leftarrow }
\math_add_sub:nn { <=> } { \Leftrightarrow }
\math_add_sub:nn { <-- } { \leftarrow }
\math_add_sub:nn { <-> } { \leftrightarrow }
\math_add_sub:nn { --> } { \rightarrow }
\math_add_sub:nn { ==> } { \Rightarrow }
\math_add_sub:nn { ... } { \dots }
% Then two letter sequences
\math_add_sub:nn { << } { \ll  }
\math_add_sub:nn { <> } { \neq }
\math_add_sub:nn { <= } { \leq }
\math_add_sub:nn { >> } { \gg  }
\math_add_sub:nn { >= } { \geq }
\math_add_sub:nn { -+ } { \mp }
\math_add_sub:nn { +- } { \pm }
\math_add_sub:nn { == } { \equiv }
\math_add_sub:nn { =. } { \doteq }
\math_add_sub:nn { =( } { \subseteq }
\math_add_sub:nn { =) } { \supseteq }
\math_add_sub:nn { =[ } { \sqsubseteq }
\math_add_sub:nn { =] } { \sqsubseteq }

% Enable substitutions for $...$ and \[...\]
\everymath { \math_grabinline:w }
\tl_put_right:Nn \[ { \math_grabdisplay:w }

\ExplSyntaxOff

\begin{document}
\centering
\newcommand*{\test}[1]{%
  $#1$%
  \[#1\]%
}
\test{a << b < c <= d >= e > f >> g}
\test{a <> b = c =. d == e}
\test{a <== b <-- c <-> d <=> e --> f ==> g}
\test{a +- b = -(-a -+ +b)}
\test{a, ..., z <> a + ...+ z}
\test{a =( b =) c =[ e =] f}
\end{document}

雖然這種方法看起來很有前途並且解決了最初方法提出的一些問題,但它有其自身的局限性。值得注意的是,替換的順序會顯著影響結果,使得解決方案彈性較差,廣泛使用時更加麻煩。此外,我嘗試創建 的別名\spec_add_sub:nn using \cs_new_eq:NN \mspecdef \spec_add_sub:nn,但它在\ExplSyntaxOn ... \ExplSyntaxOff.

我的目標是改進這種 LaTeX 定制,以獲得更強大、更靈活的命令替換系統,最好是可以在文件環境中方便使用的系統,因為序言訪問可能並不總是可用。我知道這可能是一個具有挑戰性或非常規的要求,但我相信這對 LaTeX 社區來說是一個令人著迷的問題。

我歡迎任何可以改進解決方案的見解、建議或替代方法。預先感謝您的時間和幫助。

答案1

我改進了您問題中提供的 LaTeX3 程式碼,現在它解決了您面臨的兩個問題。

  1. 它允許任意替換順序

    這是透過維護 LaTeX 中符號的前綴樹來實現的。可以透過前綴樹的後序遍歷來取得所需的替換錶。然而,正因為如此,每當插入新符號時,使用者必須呼叫\math_sub_generate:將前綴樹轉換為替換表(當然,\math_add_sub:nn可以隨時呼叫\math_sub_generate:

  2. 它支援別名

    不一致的行為可能是由於文件模式和 LaTeX3 模式之間的類別代碼差異造成的。這可以使用 來修正\tl_set_rescan:Nnn

請注意,在程式碼中,我交換了 2 個字元和 3 個字元符號的順序,結果應該仍然正確。

示範

最終的替換表如下所示:

The sequence \l_math_subs_seq contains the items (without outer braces):
>  {{<<}{\ll }}
>  {{<>}{\neq }}
>  {{<==}{\Leftarrow }}
>  {{<=>}{\Leftrightarrow }}
>  {{<=}{\leq }}
>  {{<--}{\leftarrow }}
>  {{<->}{\leftrightarrow }}
>  {{>>}{\gg }}
>  {{>=}{\geq }}
>  {{-+}{\mp }}
>  {{-->}{\rightarrow }}
>  {{+-}{\pm }}
>  {{==>}{\Rightarrow }}
>  {{==}{\equiv }}
>  {{=.}{\doteq }}
>  {{=(}{\subseteq }}
>  {{=)}{\supseteq }}
>  {{=[}{\sqsubseteq }}
>  {{=]}{\sqsubseteq }}
>  {{...}{\dots }}
>  {{@@@@@}{\mbox { }FIVE\mbox { }}}
>  {{@@@@}{\mbox { }FOUR\mbox { }}}
>  {{@@@}{\mbox { }THREE\mbox { }}}.

程式碼

\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}

\ExplSyntaxOn

\seq_new:N \l_math_subs_seq


\msg_new:nnn {math} {symbol-exists} {symbol~#1~already~exists}

\int_new:N \g_math_struct_counter
\int_gset:Nn \g_math_struct_counter {1}


\cs_new_protected:Npn \mathstruct_new:N #1
{
  \tl_set:Nx #1 {l_mathstruct_internal_\int_use:N \g_math_struct_counter _prop}
  \prop_new:c {#1}
  \int_gincr:N \g_math_struct_counter
  
  \prop_new:c {l_mathstruct_internal_\int_use:N \g_math_struct_counter _prop}
  \prop_put:cnn {#1} {value} {}
  \prop_put:cnn {#1} {exist} {\c_false_bool}
  \prop_put:cnn {#1} {mapping} {}
  \prop_put:cnx {#1} {children} {l_mathstruct_internal_\int_use:N \g_math_struct_counter _prop}
  \int_gincr:N \g_math_struct_counter
}

\mathstruct_new:N \l_math_prefix_tree_root

\tl_new:N \l_math_children_prop_tl
\tl_new:N \l_math_tmpa_tl
\tl_new:N \l_math_tmpb_tl
\tl_new:N \l_math_tmpc_tl
\cs_new_protected:Npn \math__recursive_add_sub:nnnn #1#2#3#4
 {
  
  \str_if_empty:nTF {#2} 
  {
    \prop_get:cnN {#1} {exist} \l_math_tmpa_tl
    \exp_args:NV \bool_if:nTF {\l_math_tmpa_tl}
    {
      % if symbol alreasy exists, send a warning
      \msg_warning:nnn {math} {symbol-exists} {#4}
    }
    {
      % need to set this symbol as "exists" and set the mapping
      \prop_put:cnn {#1} {exist} {\c_true_bool}
      \prop_put:cnn {#1} {mapping} {#3}
    }
  }
  {
    \prop_get:cnN {#1} {children} \l_math_children_prop_tl
    \str_set:Nx \l_math_tmpa_tl {\str_head:n {#2}}
    % see if it is one of its children
    \prop_if_in:cVTF {\l_math_children_prop_tl} \l_math_tmpa_tl
    {
      % if the node exists, continue recursively
      \tl_set:Nx \l_math_tmpc_tl {\str_tail:n {#2}}
      \prop_get:cVN {\l_math_children_prop_tl} \l_math_tmpa_tl \l_math_tmpb_tl
      \math__recursive_add_sub:Vxnn \l_math_tmpb_tl {\str_tail:n {#2}} {#3} {#4}
    }
    {
      % otherwise, need to create new node
      \mathstruct_new:N \l_math_tmpb_tl
      \prop_put:cnV {\l_math_tmpb_tl} {value} \l_math_tmpa_tl
      \prop_put:cVV {\l_math_children_prop_tl} \l_math_tmpa_tl \l_math_tmpb_tl
      % apply recursively
      \math__recursive_add_sub:Vxnn \l_math_tmpb_tl {\str_tail:n {#2}} {#3} {#4}
    }

   }
 }

\cs_generate_variant:Nn \math__recursive_add_sub:nnnn {Vxnn,VVVV}


\tl_new:N \l_math_add_tmpa_tl
\tl_new:N \l_math_add_tmpb_tl
 \cs_new_protected:Npn \math_add_sub:nn #1 #2
 {
   \tl_set_rescan:Nnn \l_math_add_tmpa_tl {\cctab_select:N\c_code_cctab} {#1}
   \tl_set_rescan:Nnn \l_math_add_tmpb_tl {\cctab_select:N\c_code_cctab} {#2}
   
   \math__recursive_add_sub:VVVV \l_math_prefix_tree_root \l_math_add_tmpa_tl \l_math_add_tmpb_tl \l_math_add_tmpa_tl
 }

% post order traversal 
 \cs_new_protected:Npn \math__sub_recursive_generate:nn #1#2
 {
  \group_begin:
    % check children first
    \prop_get:cnN {#1} {children} \l_math_children_prop_tl
    \prop_map_inline:cn {\l_math_children_prop_tl}
    {
      \math__sub_recursive_generate:nn {##2} {#2##1}
    }

    % check current node
    \prop_get:cnN {#1} {exist} \l_math_tmpa_tl
    \bool_if:nT {\l_math_tmpa_tl}
    {
      \prop_get:cnN {#1} {mapping} \l_math_tmpb_tl
      \tl_set_rescan:Nnn \l_math_tmpc_tl { \cctab_select:N \c_document_cctab } {#2}
      \seq_gput_right:Nx \l_math_subs_seq { {\exp_not:V \l_math_tmpc_tl}  {\exp_not:V \l_math_tmpb_tl} }
    }
  \group_end:
 }

% traverse the tree to get the correct order
 \cs_new_protected:Npn \math_sub_generate:
 {
  \seq_gclear:N \l_math_subs_seq
  \exp_args:NV \math__sub_recursive_generate:nn \l_math_prefix_tree_root {}
 }

\cs_new_protected:Npn \math_ascii_sub:n #1
 {
  \tl_set:Nn \l_tmpa_tl { #1 }
  \seq_map_inline:Nn \l_math_subs_seq
   {
    \tl_replace_all:Nnn \l_tmpa_tl ##1
   }
  \tl_use:N \l_tmpa_tl
 }

\cs_new_protected:Npn \math_grabinline:w #1 $
 {
  \math_ascii_sub:n { #1 } $
 }

\cs_new_protected:Npn \math_grabdisplay:w #1 \]
 {
  \math_ascii_sub:n { #1 } \]
 }


\cs_set_eq:NN \MathAddSub \math_add_sub:nn
\cs_set_eq:NN \MathGenSub \math_sub_generate: 


% Enable substitutions for $...$ and \[...\]
\everymath { \math_grabinline:w }
\tl_put_right:Nn \[ { \math_grabdisplay:w }

\ExplSyntaxOff

\begin{document}

\centering
\newcommand*{\test}[1]{%
  $#1$%
  \[#1\]%
}


\MathAddSub{ << }{ \ll  }
\MathAddSub{ <> }{ \neq }
\MathAddSub{ <= }{ \leq }
\MathAddSub{ >> }{ \gg  }
\MathAddSub{ >= }{ \geq }
\MathAddSub{ -+ }{ \mp }
\MathAddSub{ +- }{ \pm }
\MathAddSub{ == }{ \equiv }
\MathAddSub{ =. }{ \doteq }
\MathAddSub{ =( }{ \subseteq }
\MathAddSub{ =) }{ \supseteq }
\MathAddSub{ =[ }{ \sqsubseteq }
\MathAddSub{ =] }{ \sqsubseteq }
\MathAddSub{ <== }{ \Leftarrow }
\MathAddSub{ <=> }{ \Leftrightarrow }
\MathAddSub{ <-- }{ \leftarrow }
\MathAddSub{ <-> }{ \leftrightarrow }
\MathAddSub{ --> }{ \rightarrow }
\MathAddSub{ ==> }{ \Rightarrow }
\MathAddSub{ ... }{ \dots }
\MathAddSub{ int }{ \int }

% generate the substitution table based on post-order traversal of the prefix tree
\MathGenSub

\ExplSyntaxOn
% show the substitution table
\seq_show:N \l_math_subs_seq
\ExplSyntaxOff

\test{a << b < c <= d >= e > f >> g}
\test{a <> b = c =. d == e}
\test{a <== b <-- c <-> d <=> e --> f ==> g}
\test{a +- b = -(-a -+ +b)}
\test{a, ..., z <> a + ...+ z}
\test{a =( b =) c =[ e =] f}
\test{int _a^b}
\test{@@@@@ @@@@ @@@}


\MathAddSub{ @@@ }{ \mbox{~}THREE\mbox{~} }
\MathAddSub{ @@@@ }{ \mbox{~}FOUR\mbox{~} }
\MathAddSub{ @@@@@ }{ \mbox{~}FIVE\mbox{~} }


% generate the substitution table again
\MathGenSub

\ExplSyntaxOn
% show the substitution table
\seq_show:N \l_math_subs_seq
\ExplSyntaxOff

\test{@@@@@ @@@@ @@@}


\end{document}

相關內容