コマンドへの入力に基づいて文を変更するにはどうすればよいでしょうか?

コマンドへの入力に基づいて文を変更するにはどうすればよいでしょうか?

私は次のような酵素画像を含むレポートを書いています。

ここに画像の説明を入力してください

主要なコンポーネント/原子を色で説明できるように\caption、カラー ガイドを含めたいと思います (このレポートは公開されていないため、色覚異常に関しては問題になりません)。現在、次のようなコマンドを使用しています。

\newcommand\ColourGuide{%
Colour code: \textcolor{violet}{metal ion}, \textcolor{Green3}{residue}, \textcolor{teal}{ligand}, \textcolor{Yellow3}{substrate}, \textcolor{DarkOrange1}{phosphorus}, \textcolor{red}{oxygen} and \textcolor{RoyalBlue4}{nitrogen}%
}

ただし、画像によっては、すべての色のコンポーネントが揃っているわけではないので、冗長な色コードを含めるのはよくありません。入力に基づいて文章を作成するにはどうすればよいでしょうか? たとえば、次のようになります。

\ColourGuide{M}{R}{S}{O}{N}

次のように生成されます。

カラーコード: 金属イオン、残留物、基質、酸素、窒素。

指定された色で。私は を使用しておりlualatex、を使用してみましたlua(これはいいことです)が、2つを適切に(効率的に)対話する方法がわかりません。たとえば、Python で を使用する場合のように、引数を抽出するにはどうすればよいですかinput[0]?どのような方法でもアドバイスや解決策があればありがたいですが、luaそれを学習して、頻繁に使用する他のコマンドに適用し始めることができるのでうれしいですlualatex

MWE:

\documentclass{book}

\usepackage[x11names]{xcolor}

\newcommand\ColourGuide{%
Colour code: \textcolor{violet}{metal ion}, \textcolor{Green3}{residue}, \textcolor{teal}{ligand}, \textcolor{Yellow3}{substrate}, \textcolor{DarkOrange1}{phosphorus}, \textcolor{red}{oxygen} and \textcolor{RoyalBlue4}{nitrogen}%
}

\begin{document}

\ColourGuide{}

\end{document}

見た目は次のようになります:

ここに画像の説明を入力してください

(これにはどんなタグを付けたらいいのか分かりません)

答え1

あなたの回答は良い試みですが…

\documentclass{book}

\usepackage[x11names]{xcolor}

\ExplSyntaxOn

\NewDocumentCommand{\ColourGuide}{m}
 {
  Color~code:~\jamest_colourguide:n { #1 }
 }

\seq_new:N \l__jamest_colourguide_seq

\cs_new_protected:Nn \jamest_colourguide:n
 {
  \seq_clear:N \l__jamest_colourguide_seq
  \tl_map_function:nN { #1 } \__jamest_colourguide_add:n
  \seq_use:Nnnn \l__jamest_colourguide_seq { ~and~ } { ,~ } { ~and~}
 }

\cs_new_protected:Nn \__jamest_colourguide_add:n
 {
  \seq_put_right:Nx \l__jamest_colourguide_seq
   {
    \str_case:nn { #1 }
     {
      {a}{\textcolor{red}{oxygen},~\textcolor{DarkOrange1}{phosphorus},~
          \textcolor{Yellow3}{substrate},~\textcolor{Green3}{residue},~
          \textcolor{RoyalBlue4}{nitrogen},~\textcolor{teal}{ligand}~
          and~\textcolor{violet}{metal~ion}.}
      {M}{\textcolor{violet}{metal~ion}}
      {R}{\textcolor{Green3}{residue}}
      {S}{\textcolor{Yellow3}{substrate}}
      {O}{\textcolor{red}{oxygen}}
      {N}{\textcolor{RoyalBlue4}{nitrogen}}
      {L}{\textcolor{teal}{ligand}}
      {P}{\textcolor{DarkOrange1}{phosphorus}}
     }
   }
 }

\ExplSyntaxOff

\begin{document}

\noindent\ColourGuide{MRSON}

\noindent\ColourGuide{LPON}

\noindent\ColourGuide{a}

\end{document}

ここに画像の説明を入力してください

カウントは必要ありません。引数は文字ごとにマッピングされ、関連するコードがシーケンスに追加され、後で適切なセパレーターとともに使用できるようになります。

コードの重複を避けるために、完全なリストが必要な場合は引数を空のままにしておく方がよいでしょう。

\documentclass{book}

\usepackage[x11names]{xcolor}

\ExplSyntaxOn

\NewDocumentCommand{\ColourGuide}{m}
 {
  Color~code:~
  \jamest_colourguide:n { #1 }
 }

\seq_new:N \l__jamest_colourguide_seq

\cs_new_protected:Nn \jamest_colourguide:n
 {
  \tl_if_empty:nTF { #1 }
   {
    \jamest_colourguide:n { OPSRNLM }
   }
   {
    \seq_clear:N \l__jamest_colourguide_seq
    \tl_map_function:nN { #1 } \__jamest_colourguide_add:n
    \seq_use:Nnnn \l__jamest_colourguide_seq { ~and~ } { ,~ } { ~and~}
   }
 }

\cs_new_protected:Nn \__jamest_colourguide_add:n
 {
  \seq_put_right:Nx \l__jamest_colourguide_seq
   {
    \str_case:nn { #1 }
     {
      {M}{\textcolor{violet}{metal~ion}}
      {R}{\textcolor{Green3}{residue}}
      {S}{\textcolor{Yellow3}{substrate}}
      {O}{\textcolor{red}{oxygen}}
      {N}{\textcolor{RoyalBlue4}{nitrogen}}
      {L}{\textcolor{teal}{ligand}}
      {P}{\textcolor{DarkOrange1}{phosphorus}}
     }
   }
 }

\ExplSyntaxOff

\begin{document}

\noindent\ColourGuide{MRSON}

\noindent\ColourGuide{LPON}

\noindent\ColourGuide{}

\end{document}

出力は同じです。

答え2

コメントのおかげで、見た目は良くないが要求通りに機能する解決策をまとめることができました。@egregによる回答を追加コマンドの引数の文字数を決定する方法リンクされた回答@John Kormyloコメントしたこれは次のようになります:

ここに画像の説明を入力してください

\documentclass{book}

\usepackage[x11names]{xcolor}
\usepackage{xstring}

\usepackage{xparse} % loads expl3

\ExplSyntaxOn

\newcounter{Chars}
\newcounter{CharsConstant}

\def\CharsCount#1{%
    \setcounter{Chars}{\tl_count:n { #1 }}%
    \setcounter{CharsConstant}{\tl_count:n { #1 }}%
}

\NewDocumentCommand{\punctOrAnd}{mm}
    {
    \int_compare:nTF { #1 > 1 }
    {
    \int_compare:nTF { #1 = \value{CharsConstant} }
        {
            #2
            }
            {
            ,~#2
            }
    }
    {
    \space and~#2.
    }
 }

\def\ColourGuide#1{\CharsCount{#1}Colour~code:~\scanA#1\end}
\def\scanA#1{%
    \ifx\end#1\else
        \IfStrEq{#1}{a}{\textcolor{red}{oxygen},~\textcolor{DarkOrange1}{phosphorus},~\textcolor{Yellow3}{substrate},~\textcolor{Green3}{residue},~\textcolor{RoyalBlue4}{nitrogen},~\textcolor{teal}{ligand}~and~\textcolor{violet}{metal~ion}.}{}%
        %
        \IfStrEq{#1}{M}{\punctOrAnd{\value{Chars}}{\textcolor{violet}{metal~ion}}}{}%
        \IfStrEq{#1}{R}{\punctOrAnd{\value{Chars}}{\textcolor{Green3}{residue}}}{}%
        \IfStrEq{#1}{S}{\punctOrAnd{\value{Chars}}{\textcolor{Yellow3}{substrate}}}{}%
        \IfStrEq{#1}{O}{\punctOrAnd{\value{Chars}}{\textcolor{red}{oxygen}}}{}%
        \IfStrEq{#1}{N}{\punctOrAnd{\value{Chars}}{\textcolor{RoyalBlue4}{nitrogen}}}{}%
        \IfStrEq{#1}{L}{\punctOrAnd{\value{Chars}}{\textcolor{teal}{ligand}}}{}%
        \IfStrEq{#1}{P}{\punctOrAnd{\value{Chars}}{\textcolor{DarkOrange1}{phosphorus}}}{}%
        %
        \addtocounter{Chars}{-1}%
    \expandafter \scanA \fi
}

\ExplSyntaxOff

\begin{document}

\noindent\ColourGuide{MRSON}

\noindent\ColourGuide{LPON}

\noindent\ColourGuide{a}

\end{document}

答え3

これは、セミコロンを終了文字として使用します。 は、 \def\CGparse#1#2;最初のトークン (文字) を に#1、セミコロンより前のすべてを に配置します。になる#2まで、自分自身を再帰的に呼び出します。#2\empty

\documentclass{book}
\usepackage[x11names]{xcolor}

\newcommand\ColourGuide[1]{Colour code:{\count1=0\relax\CGparse#1;}}

\def\CGparse#1#2;{\def\A{#1}%
  \def\B{#2}%
  \ifnum\count1=0\relax
    \space
  \else
    \ifx\B\empty\relax
      { and }%
    \else
      {, }%
    \fi
  \fi
  \advance\count1 by 1
  \def\C{M}\ifx\A\C\relax\textcolor{violet}{metal ion}\fi
  \def\C{R}\ifx\A\C\relax\textcolor{Green3}{residue}\fi
  \def\C{L}\ifx\A\C\relax\textcolor{teal}{ligand}\fi
  \def\C{S}\ifx\A\C\relax\textcolor{Yellow3}{substrate}\fi
  \def\C{P}\ifx\A\C\relax\textcolor{DarkOrange1}{phosphorus}\fi
  \def\C{O}\ifx\A\C\relax\textcolor{red}{oxygen}\fi
  \def\C{N}\ifx\A\C\relax\textcolor{RoyalBlue4}{nitrogen}\fi
  \ifx\B\empty\relax
    {.  }%
  \else 
    \CGparse#2;
  \fi}

\begin{document}

\ColourGuide{MRSON}

\end{document}

答え4

すでに多くの優れた回答があり、おそらく私が使用しているものはpgfkeys非常に複雑で面倒ですが、これが私が思いついたものです。

  • 必要ありませんlualatex(ここでは明示的に要求されましたが、他の場所で誰かがこれを使用できる可能性があります)。
  • \ColourGuide[ligand,residue]または と書くかどうかに関係なく、原子は常に同じ順序でリストされます\ColourGuide[residue,ligand]。 これは望ましいかどうかはわかりませんが、同じ順序を維持するのが良いスタイルだと思います。

もちろん、オプションの引数/キーは、\ColourGuide[L,R]ほとんど手間をかけずに、たとえば次のように短縮することもできます。

結果

\documentclass{article}

% create 'if's for each atom
\newif\ifmetalion
\newif\ifresidue
\newif\ifligand
\newif\ifsubstrate
\newif\ifphosphorus
\newif\ifoxygen
\newif\ifnitrogen

% create two counters
\newcounter{numtrue} % total number of 'if's which are true
\newcounter{currval} % counter (for correctly putting 'and' at the last atom)

% colors
\usepackage[x11names]{xcolor}

% create pgfkeys
\usepackage{pgfkeys,xcolor}
\pgfkeys{
    /colourguide/.is family, /colourguide/.cd,
    default/.style={
        metalion=false,
        residue=false,
        ligand=false,
        substrate=false,
        phosphorus=false,
        oxygen=false,
        nitrogen=false,
    },
    metalion/.is if=metalion,
    residue/.is if=residue,
    ligand/.is if=ligand,
    substrate/.is if=substrate,
    phosphorus/.is if=phosphorus,
    oxygen/.is if=oxygen,
    nitrogen/.is if=nitrogen
}

% this will produce the adequate delimiter between different atoms
\def\chooseDelimiter{%
    \stepcounter{currval}% increment counter
    \ifnum\value{currval}<\value{numtrue}\relax%     if more than one atom remains ...
        ,\ %                                         ... put ',' ...
    \else%                                           
        \ifnum\value{currval}=\value{numtrue}\relax% ... if only one atom remains ...
            \ and\ %                                 ... put 'and' ...
        \else%                                       ... if no atom remains ...
            .%                                       ... put '.'
        \fi%
    \fi%
}

% main command: \ColorGuide
\newcommand\ColourGuide[1][]{%
    % do pgfkeys magic
    \pgfkeys{/colourguide, default, #1}%
    % get number of requested atoms
    \setcounter{currval}{1}%
    \setcounter{numtrue}{0}%
    \ifmetalion  \stepcounter{numtrue}\fi%
    \ifresidue   \stepcounter{numtrue}\fi%
    \ifligand    \stepcounter{numtrue}\fi%
    \ifsubstrate \stepcounter{numtrue}\fi%
    \ifphosphorus\stepcounter{numtrue}\fi%
    \ifoxygen    \stepcounter{numtrue}\fi%
    \ifnitrogen  \stepcounter{numtrue}\fi%
    % build sentence
    Colour code:\ %
    \ifmetalion\textcolor{violet}{metal ion}\chooseDelimiter\fi%
    \ifresidue\textcolor{Green3}{residue}\chooseDelimiter\fi%
    \ifligand\textcolor{teal}{ligand}\chooseDelimiter\fi%
    \ifsubstrate\textcolor{Yellow3}{substrate}\chooseDelimiter\fi%
    \ifphosphorus\textcolor{DarkOrange1}{phosphorus}\chooseDelimiter\fi%
    \ifoxygen\textcolor{red}{oxygen}\chooseDelimiter\fi%
    \ifnitrogen\textcolor{RoyalBlue4}{nitrogen}\chooseDelimiter\fi%
}

\begin{document}
    \paragraph{Works with one atom:}
    \ColourGuide[metalion]
    
    \paragraph{Works with two atoms:}
    \noindent\ColourGuide[residue,ligand]\\ % two atoms
    
    \paragraph{Works with many atoms:}
    \ColourGuide[metalion,residue,ligand,substrate,phosphorus,oxygen,nitrogen]\\ % many atoms
    
    \paragraph{Keeps the given order, no matter what is the order in brackets:}
    \ColourGuide[ligand, residue]
\end{document}

関連情報