9

An Automata can be

  • 1- or 2- ways,
  • With Several Heads or a Single head
  • Deterministic or Non-deterministic
  • With a stack or not

That makes 2^4 = 16 possibilities. However, for each combination, I'd like to define several macros:

  1. An abbreviation (e.g. 2MDFA+S for a "2-ways multi-head deterministic finite automata with stack")
  2. An extended version (e.g. to fully write "2-ways multi-head deterministic finite automata with stack")
  3. An abbreviation in a different font-face (e.g. 2MDFA+S)
  4. In the case of a multi-headed automata, the posibility to display the number of head in parenthesis (e.g 2MDFA+S(3) for a "2-ways 3-heads deterministic finite automata with stack")

I would like not to write at each time what I want, but to massively use macro, to prevent typographic errors and misspellings. And, of course, to be able to change easily the displaying if I want to.


I know that I can define shorthands quickly, use \csname...\endcsname or define macros that define macro but that still leave me 16 combinations to define.

I know that I can test for equality of a parameter thanks to the xstring package, but that force me to duplicate several if tests. That would (I guess) to have macros like \fa{1}{M}{D}{S}, but I don't mind.

There is the additional difficulty that I cannot just "print the name of the macro", since I want "\O..." (for "one-way") to be printed with the number "1...".


Some kind of (lousy) attempt:
\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}
Clément
  • 5,591
  • Of course, I do not want to have a solution, but just some advices on how you would proceed. – Clément Jul 08 '14 at 15:35
  • I don't know anything about “automata”, but 48 macros could be reduced to 48 —short— lines (with some “generator”), which I think is not too much. Of course, you may be able to use loops (\loop, or a more usual \foreach). But still: 48 lines is not that much. That said, I repeat, I don't know anything about what you want, I was just doing numbers :) – Manuel Jul 08 '14 at 15:42
  • After reading: would something like \automata{1MDF+S} be acceptable? – Manuel Jul 08 '14 at 15:56
  • @Manuel Of course. – Clément Jul 08 '14 at 15:57
  • @Clément How many heads are allowed? Does one digit suffice? – egreg Jul 08 '14 at 16:00
  • Well, then \automata{1MDF+S} the extended one, \automata*{…} the short version, \automata+{…} the “different font”. That's easy with xparse. Now to process the inside of the argument, some \tl_… functions from expl3 would come in handy. However I think it would be easier if you only use 1 token for each variety, that is, instead of +S just +, and then let the macro see if it sees a + or no. I will think about it later, right now I can't compile :) – Manuel Jul 08 '14 at 16:01
  • @egreg : it can be any figure or a variable (i.e. k, k+1, etc). – Clément Jul 08 '14 at 16:01

3 Answers3

12

This should be what you want. The mandatory argument to \fa should be a four character string:

  • 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)

The optional argument is the number of heads, while the macro \fa can be followed by * for printing the abbreviation in boldface or by + for the long version.

\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}

enter image description here

Note that the word is automaton (automata is plural) and there should be no “s” in the prefixes (two way, not two ways; 3-head, not 3-heads), according to English grammar.


A variation that prints the number of heads parenthesized in the long form, when it's not an explicit number and it contains more than a symbol.

\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}

enter image description here

egreg
  • 1,121,712
  • Woo. This is impressive. However, you might have made some mistake by copying / pasting, since that document cannot get compiled: I get a bunch of Undefined control sequence. And thanks for pointing my English mistake! – Clément Jul 08 '14 at 16:34
  • 1
    @Clément You need a fully up-to-date TeX system; what distribution are you running? – egreg Jul 08 '14 at 16:37
  • An old one :( That should probably be the occasion to make an update! – Clément Jul 08 '14 at 16:38
  • I manage to compile it by updating to TeXLive 2014. Now I am trying to understand the structure of your file. – Clément Jul 08 '14 at 20:49
  • @Clément I added some comments to the code – egreg Jul 08 '14 at 20:56
  • You are too fast for me! Thanks a lot for your explanation, I will play a bit with it now, and texdoc xparse a lot! – Clément Jul 08 '14 at 20:58
  • 1
    It would be useful I think to wrap k+1 in parentheses, probably with some optional arg? Or let the user add the parentheses and remove them in the short form if present? – Bruno Le Floch Jul 09 '14 at 00:19
  • @BrunoLeFloch I was thinking to this; probably checking with \regex_match:nVTF whether \l_clement_heads_tl contains just digits or not. – egreg Jul 09 '14 at 08:29
  • @egreg: I don't think that $k$-head for instance needs parentheses. I would suggest testing if the arg is surrounded by parentheses. If it is, then don't add them in the short form nor in the long form. If it is not, add them to the short form but not the long. Not sure how to best test for surrounding parentheses, as there are pitfalls: (a+b)+(c+d) starts and ends with a parenthesis but is not surrounded by them. – Bruno Le Floch Jul 10 '14 at 11:02
  • 1
    @BrunoLeFloch In my example $k$-head doesn't get parentheses: I add them only if the argument doesn't contain digits only and is more than one token long. – egreg Jul 10 '14 at 11:07
  • 1
    Ok, sorry, I should've read your code. Note that [^0-9] can be replaced by \D. – Bruno Le Floch Jul 10 '14 at 12:57
  • @BrunoLeFloch I keep forgetting those abbreviations. – egreg Jul 10 '14 at 13:00
  • @egreg Thanks for those enhancements. I tried to play a bit with xspace, but as \clement_fa_long_aux is protected, I did not manage to let the "developed version" get hyphenated. It produces large blocks of texts that cannot be spitted across two lines. – Clément Jul 10 '14 at 20:57
  • @Clément What should \xspace do? There's never use for it when the macro has arguments. The long text can be split across lines: it's just text. By the way, I tried the example with \textwidth=3cm; it is hyphenated as it should. – egreg Jul 10 '14 at 21:02
  • @egreg My bad, I added a \text that should not be there. I fixed it, thanks. – Clément Jul 10 '14 at 21:07
6

Here's an approach which is initially a little top heavy but allows you to make various formatting choices down the line:

\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}

enter image description here

I illustrate above how you can make a decision on how to format things within \ae@parse@automata@name by testing whether it's a multiheaded automaton or not.

Of course, names don't have to be names like ginger or rodger, you could just simply say

\set(1MDS)->1MDS;

and then later call it by

\get(1MDS)->name;

UPDATE

I've rewritten the above code to take into consideration the suggestions made by @egreg. Also, I've removed the dependency upon etoolbox, which is not needed to create this sort of functionality. Finally, I've improved the setter and getter to take an optional second argument. That way you can just create the automaton as

\set(1MDS);

and then call it with

\get(1MDS);

but I've left the old syntax just in case you really want to anthropomorphize your automata (go Ginger!).

A.Ellett
  • 50,533
  • Wo. This is also very impressive. – Clément Jul 08 '14 at 16:58
  • Isn't \@namedef{...} better than \x\def\csname...\endcsname? – egreg Jul 08 '14 at 17:20
  • @egreg Are there analogues to \@namedef for \edef, \gdef, and \xdef? – A.Ellett Jul 08 '14 at 17:24
  • @egreg Also, I'm not sure how I would do the \expandafter\endcsname trick with your suggesetion. – A.Ellett Jul 08 '14 at 17:28
  • \@namedef{abc\expandafter} works; for \gdef you can do \global\@namedef; there's nothing in the kernel for \edef, but you can do \providecommand\@nameedef[1]{\expandafter\edef\csname#1\endcsname}. A better alias for \expandafter is \@xp (that's used by amsmath). – egreg Jul 08 '14 at 17:31
  • 1
    @A.Ellett etoolbox has \csdef, \csedef, \csgdef and \csxdef (and a number of related commands like \csletcs...). – cgnieder Jul 08 '14 at 21:00
  • @cgnieder Thank you. I forgot about those. Though I was only using etoolbox because I was being too lazy (read sloppy) to properly handle the expansion. – A.Ellett Jul 08 '14 at 21:09
  • It also has a few useful conditionals... if you only have it for \expandonce: it's defined \newcommand\expandonce[1]{\unexpanded\expandafter{#1}} – cgnieder Jul 08 '14 at 21:16
  • Your answer is really detailed and clever, but I am less used to this syntax, that I find less handy. I will stick to egreg's solution, but I keep in mind to study yours when I will have the opportunity. – Clément Jul 08 '14 at 21:55
  • @clement The fun is in creating an answer. I wasn't fully keen on what I'd written earlier so edited the code to something more to my liking. I appreciate that you like though. :) – A.Ellett Jul 08 '14 at 21:57
4

Since there are other solutions of the “front end” here I just a the solution for parsing the argument.

This is just a simple “reader”: it reads a certain element and checks if it's equal to some predefined cases, and, in case it is not, it does what it's inside the F argument (…:nnnF).

  1. First element, 1 or 2, otherwise does nothing (no error).
  2. Second element, M or S, otherwise puts it in math mode before -head (e.g., k = $k$-head, {k+1} = $k+1$-head).
  3. Third element, D or N, otherwise does nothing.
  4. Fourth element, + or nothing (or something else, it doesn't matter). I'm not sure if you ever want an explicit “without stack” so I leave it to you.

The second element, if more than one token, must be enclosed in braces, e.g., \automata{1{(k+1)}N+}.

UPDATE: I forgot some part of the “sentence”.

\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}

enter image description here

Manuel
  • 27,118
  • 1
    Beware that I don't know anything about expansion. Reading egreg's answer I see that he uses \str_case:nn when I use (at some point) \str_case_x:nn; he also uses \cs_new_protected:Npn while I didn't protect mine… This may be “wrong” in other cases (while works well in the lines I wrote :P). – Manuel Jul 08 '14 at 17:42
  • I must admit it is hard for me to understand precisely the benefit of your modification. – Clément Jul 08 '14 at 20:43
  • 1
    The + instead of +S is because it's much more easier to check one token than two (at least in my approach). Now, the only modification is (unless I failed to understand, or made an error) the “second token”. Is that what you don't understand? If you ask a more precise question I will try to answer it. However, I think it's relatively easy to understand how the code works. – Manuel Jul 08 '14 at 20:58
  • @Clément I was adding a more detailed answer, but internet went off and I lose it :( I thought TeX.SX saved some sort of drafts… but I can't recover the text. If you have any doubt about anything don't hesitate to ask ;) – Manuel Jul 08 '14 at 21:28
  • Sorry for the waste of time. I do not think it is necessary to write the explanation again, at least not just for me. Thanks anyway! – Clément Jul 08 '14 at 22:00