我正在编写命令,使用 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}
版本包含以下内容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}
原始方法
\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\)}
[对于这种原始方法]。