Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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}
share|improve this question
    
Of course, I do not want to have a solution, but just some advices on how you would proceed. –  Clément Jul 8 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 8 at 15:42
    
After reading: would something like \automata{1MDF+S} be acceptable? –  Manuel Jul 8 at 15:56
    
@Manuel Of course. –  Clément Jul 8 at 15:57
    
@Clément How many heads are allowed? Does one digit suffice? –  egreg Jul 8 at 16:00

3 Answers 3

up vote 9 down vote accepted

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

share|improve this answer
    
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 8 at 16:34
1  
@Clément You need a fully up-to-date TeX system; what distribution are you running? –  egreg Jul 8 at 16:37
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 9 at 0:19
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 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 at 12:57

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!).

share|improve this answer
    
Wo. This is also very impressive. –  Clément Jul 8 at 16:58
    
Isn't \@namedef{...} better than \x\def\csname...\endcsname? –  egreg Jul 8 at 17:20
    
@egreg Are there analogues to \@namedef for \edef, \gdef, and \xdef? –  A.Ellett Jul 8 at 17:24
    
@egreg Also, I'm not sure how I would do the \expandafter\endcsname trick with your suggesetion. –  A.Ellett Jul 8 at 17:28
1  
@A.Ellett etoolbox has \csdef, \csedef, \csgdef and \csxdef (and a number of related commands like \csletcs...). –  cgnieder Jul 8 at 21:00

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_x:nnF { \tl_item:nn {#1} {#2} }
  }
\cs_new:Npn \clement_check_item:nnn #1 #2
  {
    \str_case_x: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

share|improve this answer
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 8 at 17:42
    
I must admit it is hard for me to understand precisely the benefit of your modification. –  Clément Jul 8 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 8 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 8 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 8 at 22:00

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.