저는 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^{+}\]
이 문제가 해결되기는 하지만 이상적인 요구 사항은 아닙니다. 또한 대체는 물결표 ~ 문자의 기능에 실수로 영향을 미쳐 숫자 누락과 같은 오류가 발생하고 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 코드를 개선했으며 이제는 귀하가 직면한 두 가지 문제를 해결합니다.
임의의 대체 순서를 허용합니다.
이는 LaTeX의 기호에 대한 접두사 트리를 유지함으로써 가능합니다. 원하는 대체 테이블은 접두사 트리의 사후 순회를 통해 얻을 수 있습니다. 그러나 이로 인해 새로운 기호가 삽입될 때마다 사용자는
\math_sub_generate:
접두사 트리를 대체 테이블로 변환하기 위해 호출을 해야 합니다(물론\math_add_sub:nn
항상 호출 가능\math_sub_generate:
).별칭을 지원합니다.
일관되지 않은 동작은 문서 모드와 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}