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.

I want to write a command that responds to an argument being in one set of reference strings or another.

The argument takes a (musical) note name, and the command will print an accidental depending on whether the argument is in (a, h, c, d, e, f, g), (ces, des, es, fes, ges, as, b) or (cis, dis, eis, fis, gis, ais, his) (German note names).

In a programming language I would use the in operator to achieve this, but I don't know how to do it in LaTeX.

What I would like to write is

\notewithacc{c}\\
\notewithacc{cis}\\
\notewithacc{des}

and get

\natural c\\
\sharp cis\\
\flat des

as the output.

share|improve this question
    
Related: tex.stackexchange.com/questions/89397/… –  egreg Jan 22 at 11:45
add comment

3 Answers

up vote 12 down vote accepted

The following example uses \in@{<substring>}{<string>} that tests, whether <substring> is part of <string>. After the test, the result is provided by the switch \ifin@.

\documentclass{article}

% List with notes
\newcommand*{\NotesNatural}{a, h, c, d, e, f, g}
\newcommand*{\NotesFlat}{ces, des, es, fes, ges, as, b}
\newcommand*{\NotesSharp}{cis, dis, eis, fis, gis, ais, his}

\makeatletter

% Sanitize note lists
\newcommand*{\NotesNormalize}[1]{%
  \edef#1{%
    ,\expandafter\zap@space#1 \@empty,%
  }%
}

\NotesNormalize{\NotesNatural}
\NotesNormalize{\NotesFlat}
\NotesNormalize{\NotesSharp}

% \NotesTest{<note>}{<note list>}{<yes>}{<no>}
\newcommand*{\NotesTest}[2]{%
  \edef\NotesTemp{%
    \noexpand\in@{,#1,}{#2}%
  }\NotesTemp
  \ifin@
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\newcommand*{\notewithacc}[1]{%
  \in@{,}{#1}%
  \ifin@
    ?#1%
  \else
    \NotesTest{#1}{\NotesNatural}{%
      $\m@th\natural$#1%
    }{%
      \NotesTest{#1}{\NotesFlat}{%
        $\m@th\flat$#1%
      }{%
        \NotesTest{#1}{\NotesSharp}{%
          $\m@th\sharp$#1%  
        }{%
          ?#1%
        }%
      }%
    }%
  \fi   
}
\makeatother

\begin{document}
  \noindent
  \notewithacc{c}\\
  \notewithacc{cis}\\
  \notewithacc{des}\\
  \notewithacc{x}\\
  \notewithacc{dis,eis}
\end{document}

Result

Remarks:

  • \zap@space removes spaces in the sanitizing step from the note lists.
  • Also the lists all start and ends with commas. This way, the substring with the searched note is surrounded by commas. This ensures exact matches.
  • BTW, I do not know, why accidentals are math symbols in LaTeX. \m@th removes surrounding space if \mathsurround is set.
  • The updated solution also checks, whether there is a comma in the argument of \notewithacc as in \notewithacc{dis,eis}.
share|improve this answer
    
Thank you for this solution. It works perfectly, although I don't fully understand it. The math/spacing issue is dealt with fontspec/lilyglyphs in the real files BTW (looks better anyway). –  uli_1973 Jan 22 at 12:32
    
Is there an easy way to recognize the input like \notewithacc{dis,eis} as an error as well? –  g.kov Jan 22 at 14:51
    
@g.kov: Yes, I have updated the answer by adding a check for the comma in the argument of \notewithacc. –  Heiko Oberdiek Jan 22 at 15:56
add comment

Taking a similar approach to Heiko's answer but using the LaTeX3 programming layer, expl3, might lead to something like:

\documentclass{article}

\usepackage{expl3,xparse}
\ExplSyntaxOn
\makeatletter
% Comma lists of notes
\clist_const:Nn \c_ulinotes_natural_clist {a, h, c, d, e, f, g}
\clist_const:Nn \c_ulinotes_flat_clist {ces, des, es, fes, ges, as, b}
\clist_const:Nn \c_ulinotes_sharp_clist {cis, dis, eis, fis, gis, ais, his}

\cs_new_protected:Npn \ulinotes_note:n #1
  {
    \clist_if_in:NnT \c_ulinotes_natural_clist {#1}
      { \c_math_toggle_token \m@th \natural \c_math_toggle_token }
    \clist_if_in:NnT \c_ulinotes_flat_clist {#1}
      { \c_math_toggle_token \m@th \flat \c_math_toggle_token }
    \clist_if_in:NnT \c_ulinotes_sharp_clist {#1}
      { \c_math_toggle_token \m@th \sharp \c_math_toggle_token }
  #1
  }

\NewDocumentCommand { \notewithacc } { >{\TrimSpaces} m }
  { \ulinotes_note:n {#1} }
 \makeatother
\ExplSyntaxOff

\begin{document}
  \noindent
  \notewithacc{c}\\
  \notewithacc{cis}\\
  \notewithacc{des}\\
  \notewithacc{x}
\end{document}

This uses the expl3 tests for comma-list membership along with a built-in command to strip spaces from arguments when 'grabbed'. As in the 'standard' LaTeX2e approach we have to allow for the fact that math mode is required for the symbols, which currently means using a bit of LaTex2e code (\m@th).

share|improve this answer
    
I might raise the space question for searching with the team :-) –  Joseph Wright Jan 22 at 12:12
    
Thank you. I could accept this answer too but take the other because I'm still stuck with "old" latex ;-) –  uli_1973 Jan 22 at 12:30
add comment

MWE using \csname mapping:

\documentclass{article}

\makeatletter
\@for\@n@te:=a,h,c,d,e,f,g\do{\expandafter\def\csname n@te(\@n@te)\endcsname{\natural}}
\@for\@n@te:=ces,des,es,fes,ges,as,b\do{\expandafter\def\csname n@te(\@n@te)\endcsname{\flat}}
\@for\@n@te:=cis,dis,eis,fis,gis,ais,his\do{\expandafter\def\csname n@te(\@n@te)\endcsname{\sharp}}

\def\notewithacc#1{%
\ifcsname n@te(#1)\endcsname%
\ensuremath{\csname n@te(#1)\endcsname}#1%
\else ?#1 %
\fi
}
\makeatother

\begin{document}

\makeatletter
\begin{itemize}
\item \@for\@n@te:=a,h,c,d,e,f,g\do{\notewithacc{\@n@te} }
\item \@for\@n@te:=ces,des,es,fes,ges,as,b\do{\notewithacc{\@n@te} }
\item \@for\@n@te:=cis,dis,eis,fis,gis,ais,his\do{\notewithacc{\@n@te} }
\item \@for\@n@te:=x,fi,{dis,eis}\do{\notewithacc{\@n@te} }
\end{itemize}
\makeatother

\notewithacc{des}

\end{document}

enter image description here

share|improve this answer
add comment

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.