自动机可以
- 1 或 2 路,
- 多头或单头
- 确定性或非确定性
- 有无堆栈
这样就有 2^4 = 16 种可能性。但是,对于每种组合,我想定义几个宏:
- 缩写(例如 2MDFA+S 代表“带堆栈的双向多头确定性有限自动机”)
- 扩展版本(例如,完整编写“带堆栈的双向多头确定性有限自动机”)
- 不同字体的缩写(例如2MDFA+S)
- 对于多头自动机,可以在括号中显示头的数量(例如,2MDFA+S(3) 表示“带堆栈的 2 路 3 头确定性有限自动机”)。
我不想每次都写自己想写的内容,而是大量使用宏,以防止排版错误和拼写错误。当然,如果我想的话,还可以轻松更改显示内容。
我知道我可以快速定义简写,使用\csname...\endcsname
或者定义定义宏的宏但仍然有 16 种组合需要定义。
我知道我可以测试参数是否相等感谢这个xstring
软件包,但这迫使我重复几次if
测试。那(我猜)会有像 \fa{1}{M}{D}{S} 这样的宏,但我不介意。
还有一个额外的困难是我不能仅仅“打印宏的名称”,因为我想将“\O...”(表示“单向”)与数字“1...”一起打印。
某种(糟糕的)尝试:
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{xparse}
% Underline for the example, but that could be a different font.
\newcommand{\bold}[1]{\underline{#1}}
% This part allows to have as an optional argument the number of heads.
% This is item 4 in the question.
\DeclareDocumentCommand{\abbrFA}{m g}{%
\text{#1}
\IfNoValueTF{#2}% then
{}% else
{(#2)}% fi
}
\DeclareDocumentCommand{\OMDFAS}{g}{%
\def\notation{$1$MDFA+S}
\IfNoValueTF{#1}% then
{\abbrFA{\notation}}% else
{\abbrFA{\notation}{#1}}% fi
}
\newcommand{\longOMDFAS}{$1$-way Multi-Head Finite Automata with Stack}
% How to get this automatically for every combination?
\begin{document}
\(\OMDFAS\)
\(\OMDFAS{k}\)
\longOMDFAS
\(\bold{\OMDFAS}\)
\end{document}
答案1
这应该就是你想要的。 的强制参数\fa
应该是四个字符的字符串:
- 第一个字节:1 或 2 (单向或双向)
- 第二个字节:S 或 M (单头或多头)
- 第三个字节:D 或 N(确定性或非确定性)
- 第四个字节:+ 或 - (有或无堆栈)
可选参数是头部的数量,而宏后面\fa
可以跟用于*
以粗体打印缩写或用于+
长版本。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\fa}{ s t+ m o }
{
\IfNoValueTF{#4}
{% there is no trailing optional argument, set the boolean to false
\bool_set_false:N \l_clement_show_heads_bool
}
{% there is a trailing optional argument, set the boolean to false
\bool_set_true:N \l_clement_show_heads_bool
% and store the argument for later usage
\tl_set:Nn \l_clement_heads_tl { $#4$ }
}
\IfBooleanTF{#2}
{% the `+` modifier has been given, call the long version
\clement_fa_long:n { #3 }
}
{% no + modifier
\IfBooleanTF{#1}
{% the `*` modifier has been given, call the short version in \textbf
\textbf{\clement_fa_short:n { #3 }}
}
{% no `*` modifier, just call the short version
\clement_fa_short:n { #3 }
}
}
}
% First byte: 1 or 2 (one way or two way)
% Second byte: S or M (single or multi head)
% Third byte: D or N (deterministic or non deterministic)
% Fourth byte: + or - (with or without stack)
\bool_new:N \l_clement_show_heads_bool
\tl_new:N \l_clement_heads_tl
\cs_new_protected:Npn \clement_fa_short:n #1
{% separate the four characters
\clement_fa_short_aux:NNNN #1
}
\cs_new_protected:Npn \clement_fa_short_aux:NNNN #1 #2 #3 #4
{% just use #1, #2 and #3 and add FA
#1#2#3FA
\str_case:nn { #4 }
{% check if the fourth character is - or +
{-}{}% -: do nothing
{+}{+S}% +: add +S
}
% if the boolean is true, add the number of heads in parentheses
\bool_if:NT \l_clement_show_heads_bool { (\l_clement_heads_tl) }
}
\cs_new_protected:Npn \clement_fa_long:n #1
{% separate the four characters
\clement_fa_long_aux:NNNN #1
}
\cs_new_protected:Npn \clement_fa_long_aux:NNNN #1 #2 #3 #4
{% add 1-way or 2-way and a space
#1-way~%
\bool_if:NTF \l_clement_show_heads_bool
{% if the boolean is true, add the number of heads
\l_clement_heads_tl
}
{% else add S if single-head, M if multi-head
\str_case:nn { #2 }
{
{S}{single}
{M}{multi}
}
}
-head~% add -head and a space
\str_case:nn { #3 }
{% add `non` and a space if nondeterministic
{N}{non~}
{D}{}
}
deterministic~finite~automaton~% the words
\str_case:nn { #4 }
{% add `with` or `without`
{+}{with~}
{-}{without~}
}
stack% add `stack`
}
\ExplSyntaxOff
\begin{document}
\fa{2MD+} --- \fa+{2MD+}
\fa{2MD-}[3] --- \fa+{2MD-}[3]
\fa*{2MN+}[k+1] --- \fa+{2MN-}[k+1]
\end{document}
请注意这个词是自动机(automata 是复数),并且前缀中不应该有“s”(双向, 不是两种方式;3-head,而不是3-heads,这符合英语语法。
当它不是一个明确的数字并且包含多个符号时,会打印长格式中括号内的头部数量的变化。
\documentclass{article}
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\NewDocumentCommand{\fa}{ s t+ m o }
{
\IfNoValueTF{#4}
{% there is no trailing optional argument, set the boolean to false
\bool_set_false:N \l_clement_show_heads_bool
}
{% there is a trailing optional argument, set the boolean to false
\bool_set_true:N \l_clement_show_heads_bool
% and store the argument for later usage
\tl_set:Nn \l_clement_heads_tl { #4 }
}
\IfBooleanTF{#2}
{% the `+` modifier has been given, call the long version
\clement_fa_long:n { #3 }
}
{% no + modifier
\IfBooleanTF{#1}
{% the `*` modifier has been given, call the short version in \textbf
\textbf{\clement_fa_short:n { #3 }}
}
{% no `*` modifier, just call the short version
\clement_fa_short:n { #3 }
}
}
}
% First byte: 1 or 2 (one way or two way)
% Second byte: S or M (single or multi head)
% Third byte: D or N (deterministic or non deterministic)
% Fourth byte: + or - (with or without stack)
\bool_new:N \l_clement_show_heads_bool
\tl_new:N \l_clement_heads_tl
\cs_new_protected:Npn \clement_fa_short:n #1
{% separate the four characters
\clement_fa_short_aux:NNNN #1
}
\cs_new_protected:Npn \clement_fa_short_aux:NNNN #1 #2 #3 #4
{% just use #1, #2 and #3 and add FA
#1#2#3FA
\str_case:nn { #4 }
{% check if the fourth character is - or +
{-}{}% -: do nothing
{+}{+S}% +: add +S
}
% if the boolean is true, add the number of heads in parentheses
\bool_if:NT \l_clement_show_heads_bool { \__clement_parens:V \l_clement_heads_tl }
}
\cs_new_protected:Npn \clement_fa_long:n #1
{% separate the four characters
\clement_fa_long_aux:NNNN #1
}
\cs_new_protected:Npn \clement_fa_long_aux:NNNN #1 #2 #3 #4
{% add 1-way or 2-way and a space
#1-way~%
\bool_if:NTF \l_clement_show_heads_bool
{% if the boolean is true, add the number of heads
\tl_if_single:NTF \l_clement_heads_tl
{
\__clement_simple:V \l_clement_heads_tl
}
{% \D matches anything which is not a digit
\regex_match:nVTF { \D } \l_clement_heads_tl
{ \__clement_parens:V \l_clement_heads_tl }
{ \__clement_simple:V \l_clement_heads_tl }
}
}
{% else add S if single-head, M if multi-head
\str_case:nn { #2 }
{
{S}{single}
{M}{multi}
}
}
-head~% add -head and a space
\str_case:nn { #3 }
{% add `non` and a space if nondeterministic
{N}{non~}
{D}{}
}
deterministic~finite~automaton~% the words
\str_case:nn { #4 }
{% add `with` or `without`
{+}{with~}
{-}{without~}
}
stack% add `stack`
}
\cs_new:Npn \__clement_parens:n #1 { $( #1 )$ }
\cs_new:Npn \__clement_simple:n #1 { $ #1 $ }
\cs_generate_variant:Nn \__clement_parens:n { V }
\cs_generate_variant:Nn \__clement_simple:n { V }
\cs_generate_variant:Nn \regex_match:nnTF { nV }
\ExplSyntaxOff
\begin{document}
\fa{2MD+} --- \fa+{2MD+}
\fa{2MD-}[3] --- \fa+{2MD-}[3]
\fa{1MD+}[24] --- \fa+{1MN+}[24]
\fa{2MN+}[k] --- \fa+{2MN+}[k]
\fa*{2MN+}[k+1] --- \fa+{2MN+}[k+1]
\end{document}
答案2
这里有一个方法,虽然最初有点头重脚轻,但可以让您在后面做出各种格式选择:
\documentclass{article}
\makeatletter
\let\@xp\expandafter
\newcommand\set{\ae@set}
\def\ae@set(#1){%%
\@ifnextchar;%%
{\@ae@set(#1)}%%
{\ae@@set(#1)}}
\def\@ae@set(#1);{\ae@@set(#1)->#1;}
\def\ae@@set(#1)->#2;{%%
\def\ae@name{#1}%%
\ae@parse#2;%%
}
\def\ae@parse#1#2#3#4;{%%
\@namedef{automata:\ae@name:way}{#1}%%
\@namedef{automata:\ae@name:head}{#2}%%
\@namedef{automata:\ae@name:det}{#3}%%
\@namedef{automata:\ae@name:stack}{#4}%%
\ae@parse@automata@name
\ae@parse@meaning
\@namedef{automata:\ae@name:name\@xp}\@xp{\ae@automata@name}%%
\@namedef{automata:\ae@name:meaning\@xp}\@xp{\ae@meaning}%%
}
\def\ae@test@for@formatting#1#2#3#4{%%
\def\ae@test{#1}%%
\@xp\ifx\csname automata:\ae@name:#2\endcsname\ae@test
\def\ae@formatting{#3}%%
\else
\def\ae@formatting{#4}%%
\fi}
\def\ae@parse@automata@name{%%
\def\ae@formatting{}%%
\ae@test@for@formatting{M}{head}{\bfseries\sffamily}{\itshape}%%
\@xp\def\@xp\ae@automata@name\@xp{%%
\@xp{\ae@formatting
\csname automata:\ae@name:way\endcsname
\csname automata:\ae@name:head\endcsname
\csname automata:\ae@name:det\endcsname
\csname automata:\ae@name:stack\endcsname}}}
\def\ae@test@for@meaning#1#2#3#4{%%
\def\ae@test{#1}%%
\@xp\ifx\csname automata:\ae@name:#2\endcsname\ae@test
\edef\ae@meaning{\ae@meaning\space #3}%%
\else
\edef\ae@meaning{\ae@meaning\space #4}%%
\fi}
\def\ae@parse@meaning{%%
\def\ae@meaning{}%%
\ae@test@for@meaning{1}{way}{1-way,}{2-way,}%%
\ae@test@for@meaning{M}{head}{multi-head,}{single-head,}%%
\ae@test@for@meaning{D}{det}{deterministic}{non-deterministic}%%
\ae@test@for@meaning{S}{stack}{with stack}{without stack}%%
}
\newcommand\get{\ae@get}
\def\ae@get(#1){%%
\@ifnextchar;%%
{\@ae@get(#1)}%%
{\ae@@get(#1)}}
\def\@ae@get(#1);{\ae@@get(#1)->meaning;}
\def\ae@@get(#1)->#2;{\csname automata:#1:#2\endcsname}
\makeatother
\begin{document}
\set(ginger)->1MDS;
\set(rodger)->2SNS;
\set(1MDN);
\begin{tabular}{llll}
Nickname & Sample getter & Name & Meaning \\\hline
Ginger & \get(ginger)->way; & \get(ginger)->name; & \get(ginger)->meaning;\\
Rodger & \get(rodger)->det; & \get(rodger)->name; & \get(rodger)->meaning;\\
1MDN & \get(1MDN)->det; & \get(1MDN)->name; & \get(1MDN);
\end{tabular}
\end{document}
\ae@parse@automata@name
我在上面说明了如何通过测试它是否是多头自动机来决定如何格式化其中的事物。
当然,names
不必像ginger
或这样的名字rodger
,你可以简单地说
\set(1MDS)->1MDS;
然后通过以下方式调用它
\get(1MDS)->name;
更新
我重写了上述代码,以考虑@egreg 的建议。此外,我还删除了对 的依赖etoolbox
,这对于创建此类功能来说不是必需的。最后,我改进了二传手和吸气剂采取选修的第二个参数。这样,你就可以创建自动机
\set(1MDS);
然后调用它
\get(1MDS);
但是我保留了旧的语法,以防万一你真的想将你的自动机拟人化(去 Ginger!)。
答案3
由于这里还有其他“前端”的解决方案,因此我仅介绍解析这个论点。
这只是一个简单的“读取器”:它读取某个元素并检查它是否等于某些预定义的情况,如果不相等,它就执行参数中的内容F
(…:nnnF
)。
- 第一个元素,
1
或2
,否则不执行任何操作(无错误)。 - 第二个元素,
M
或S
,否则将其置于数学模式之前-head
(例如,k
=$k$-head
,{k+1}
=$k+1$-head
)。 - 第三个元素,
D
或N
,否则不执行任何操作。 - 第四个元素,
+
或者什么都没有(或者其他东西,无所谓)。我不确定你是否想要一个明确的“无堆栈”,所以我把它留给你了。
第二个元素(如果有多个标记)必须用括号括起来,例如\automata{1{(k+1)}N+}
。
更新:我忘记了这个“句子”的某些部分。
\documentclass{scrartcl}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \automata { m }
{
\clement_automata_process:n {#1}
}
\cs_new:Npn \clement_check_item:nnnF #1 #2
{
\str_case_e:nnF { \tl_item:nn {#1} {#2} }
}
\cs_new:Npn \clement_check_item:nnn #1 #2
{
\str_case_e:nn { \tl_item:nn {#1} {#2} }
}
\cs_new_nopar:Npn \clement_automata_process:n #1
{
\clement_check_item:nnn {#1} { 1 }
{
{ 1 } { $1$-way }
{ 2 } { $2$-way }
} ~
\clement_check_item:nnnF {#1} { 2 }
{
{ S } { single-head }
{ M } { multiple-head }
}
{ $\tl_item:nn {#1} { 2 }$-head } ~
\clement_check_item:nnn {#1} { 3 }
{
{ D } { deterministic }
{ N } { non-deterministic }
} ~ finite ~ automaton ~
\clement_check_item:nnn {#1} { 4 }
{
{ + } { with ~ stack }
% { - } { without ~ stack} % if it's needed you can uncomment it
} % Or you can add it here if you want it (nnnF) always “without” unless `+` is given
}
\ExplSyntaxOff
\begin{document}
\obeylines
\automata{1MD+}
\automata{2kN}
\automata{2{(k+1)}N+}
\end{document}