定义一个照应宏来根据模板定义多个命令

定义一个照应宏来根据模板定义多个命令

我正在编写命令,使用 TikZ 为吉他排版和弦表。因为每个和弦都有两个等效(等音)名称,我想在定义它们时避免冗余,所以我想编写一个宏,为每个和弦生成略有不同的代码。然而,与这个问题,由于排版和弦名称需要包含在 TikZ 代码中,因此我需要处理一个附加参数,该参数可能因命令名称的每个版本而异。

由于我使用 TikZ,所以我的方法是使用双变量\foreach循环和一种宏指令照应 \newchord(顺便说一句,我发现这个术语在 TeX 中显然并不常见,这很有趣),它使用一个\chordname命令,该命令应该在每个定义中扩展为相应的名称:

\documentclass{article}
\usepackage{tikz}

\newcommand{\newchord}[2]{%
  \foreach \command / \name in {#1} {%
    \expandafter\gdef\csname \command\endcsname{%
      \def\chordname{\name}
      #2
    }
  }
}

% this is how I want to use the macro:
\newchord{ASharp/{A\(\sharp\)}, BFlat/{B\(\flat\)}}{%
  % here, actually some TikZ drawing will happen
  I am: \chordname
}

\begin{document}
\ASharp % -> I am: A♯
\BFlat % -> I am: B♭
\end{document}

此示例失败

! 未定义的控制序列。

\弦名 ->\名称

l.20 \ASharp

我怀疑这是因为\chordname扩张得太早了,但老实说,我对扩张确实不太了解,无论如何......

我不介意不使用 的解决方案\foreach,这只是我发现的最简单的入门方法。另外,使用的方式\chordname只是一个想法——只要解决方案的工作方式与我的使用示例类似,就可以了(但让它工作起来会很有趣)。斜线作为分隔符也不重要,它只是\foreach工作所需的。

答案1

常见问题:您必须扩展\command\name

\documentclass{article}
\usepackage{tikz}

\newcommand{\newchord}[2]{%
  \foreach \command/\name in {#1} {%
    \expandafter\xdef\csname \command\endcsname{%
      \def\noexpand\chordname{\unexpanded\expandafter{\name}}%
      \unexpanded{#2}%
    }%
  }%
}

% this is how I want to use the macro:
\newchord{ASharp/{A\(\sharp\)}, BFlat/{B\(\flat\)}}{%
  % here, actually some TikZ drawing will happen
  I am: \chordname
}

\begin{document}
\ASharp % -> I am: A♯

\BFlat % -> I am: B♭
\end{document}

enter image description here

版本包含以下内容expl3

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\newchord}{mm}
 {
  \clist_map_inline:nn { #1 }
   {
    \phg_newchord:nn { ##1 } { #2 }
   }
 }
\cs_new_protected:Nn \phg_newchord:nn
 {
  \__phg_newchord:nw {#2} #1 \q_stop
 }
\cs_new_protected:Npn \__phg_newchord:nw #1 #2/#3 \q_stop
 {
  \cs_new:cpn { #2 }
   {
    \cs_set:Npn \chordname { #3 }
    #1
   }
 }
\ExplSyntaxOff

% this is how I want to use the macro:
\newchord{
  ASharp/A\(\sharp\),
  BFlat/B\(\flat\)
}{%
  % here, actually some TikZ drawing will happen
  I am: \chordname
}

\begin{document}
\ASharp % -> I am: A♯

\BFlat % -> I am: B♭
\end{document}

第二个版本的语法略有不同(但我认为更好):

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\newchord}{mm}
 {
  \clist_map_inline:nn { #1 }
   {
    \phg_newchord:nn { ##1 } { #2 }
   }
 }

\tl_new:N \l__phg_newchord_replacement_tl

\cs_new_protected:Nn \phg_newchord:nn
 {
  \tl_set:Nn \l__phg_newchord_replacement_tl { #2 }
  \keys_set:nn { phg/newchord } { #1 }
 }
\keys_define:nn { phg/newchord}
 {
  unknown .code:n = \__phg_newchord_define:Vn \l_keys_key_tl { #1 }
 }
\cs_new_protected:Nn \__phg_newchord_define:nn
 {
  \cs_new_protected:cpx { #1 }
   {
    \cs_set:Npn \exp_not:N \chordname { \exp_not:n { #2 } }
    \exp_not:V \l__phg_newchord_replacement_tl
   }
 }
\cs_generate_variant:Nn \__phg_newchord_define:nn { V }
\ExplSyntaxOff

% this is how I want to use the macro:
\newchord{
  ASharp = A\(\sharp\),
  BFlat = B\(\flat\),
}{%
  % here, actually some TikZ drawing will happen
  I am: \chordname
}

\begin{document}
\ASharp % -> I am: A♯

\BFlat % -> I am: B♭
\end{document}

答案2

您可以使用pgfkeys及其.list处理程序来避免全局定义:

\documentclass{article}
\usepackage{tikz}

\pgfkeys{
  chords/.is family,chords,
  define/.code args={#1/#2/#3}{
    \expandafter\def\csname#1\endcsname{%
      \def\chordname{#2}
      #3
    }
  },
}
\newcommand{\newchord}[2]{%
  \pgfkeys{chords,
    define hardcode/.style={define=##1/#2},
    define hardcode/.list={#1},
  }
}

% this is how I want to use the macro:
\newchord{ASharp/{A\(\sharp\)}, BFlat/{B\(\flat\)}}{%
  % here, actually some TikZ drawing will happen
  I am: \chordname
}

\begin{document}
\ASharp % -> I am: A♯
\BFlat % -> I am: B♭
\end{document}

答案3

expl3带有定义的直接扩展的版本

\documentclass{article}
\usepackage{tikz}

\usepackage{expl3}


\ExplSyntaxOn

\cs_new:Npn \newchord #1 {%
  \seq_set_from_clist:Nn \l_tmpa_seq {#1}
  \seq_map_inline:Nn \l_tmpa_seq {%
    \seq_set_split:Nnn \l_tmpb_seq {/} {##1} 
    \cs_gset:cpx {\seq_item:Nn \l_tmpb_seq {1}} {\seq_item:Nn \l_tmpb_seq {2}}
  }
}
\ExplSyntaxOff

\newchord{ASharp/{A\(\sharp\)}, BFlat/{B\(\flat\)}}{%
  % here, actually some TikZ drawing will happen
  %I am: \chordname
}

\begin{document}
\ASharp % -> I am: A♯

\BFlat % -> I am: B♭
\end{document}

答案4

以下是使用listofitems包来解析数据的一种方法。

已编辑,以提供 OP 指定的语法。

\documentclass{article}
\usepackage{listofitems}
\newtoks\Chordtoks
\newtoks\ChordDatatoks
\newcommand{\addtotoks}[2]{#1\expandafter{\the#1#2}}
\newcommand{\xaddtotoks}[2]{\expandafter\addtotoks\expandafter#1\expandafter{#2}}
\newcommand\newchord[2]{%
  \setsepchar{,}%
  \readlist*\ChordList{#1}%
  \foreachitem\x\in\ChordList{%
    \setsepchar[$]{/}%
    \readlist*\ChordData{\x}%
    \ChordDatatoks\expandafter\expandafter\expandafter{\ChordData[2]}%
    \Chordtoks{\def\chordname}%
    \xaddtotoks\Chordtoks{\the\ChordDatatoks}%
    \addtotoks\Chordtoks{#2}%
    \def\tmp{\expandafter\def\csname\ChordData[1]\endcsname}%
    \expandafter\tmp\expandafter{\the\Chordtoks}%
  }%
}
\newchord{ASharp/{A\(\sharp\)}, BFlat/{B\(\flat\)}}{%
  % here, actually some TikZ drawing will happen
  I am: \chordname
}
\begin{document}
\ASharp

\BFlat
\end{document}

enter image description here


原始方法

\documentclass{article}
\usepackage{listofitems}
\newcommand\newchord[2]{%
  \setsepchar{,}%
  \readlist*\ChordList{#1}%
  \foreachitem\x\in\ChordList{%
    \setsepchar[$]{/}%
    \readlist*\ChordData{\x}%
    \def\tmp{\expandafter\def\csname\ChordData[1]\endcsname}%
    \expandafter\expandafter\expandafter\tmp%
    \expandafter\expandafter\expandafter{\ChordData[2]}%
    \def\chordname{\csname\ChordData[1]\endcsname}%
    #2\par
  }%
}
\begin{document}
\newchord{ASharp/{A\(\sharp\)}, BFlat/{B\(\flat\)}}{%
  % here, actually some TikZ drawing will happen
  I am: \chordname
}
\end{document}

我已经单独验证了\ASharp包含令牌{A\(\sharp\)},同样,也\BFlat包含令牌{B\(\flat\)}[对于这种原始方法]。

相关内容