我需要根据输入是否包含空格或连字符来区别对待。也就是说,以下
\foo{foobar}
\foo{foo bar baz}
\foo{foo bar-baz}
\foo{foo-bar baz}
\foo{foo-bar-baz}
应该成为
\fooA{foobar}
\fooB{foo}\fooC{bar baz}
\fooB{foo}\fooC{bar-baz}
\fooD{foo}\fooE{bar baz}
\fooD{foo}\fooE{bar-baz}
我怎样才能实现这一点,即如何定义\foo
?
答案1
这里是介绍listofitems
解析输入包的好地方。我最近介绍了一个包getargs
,几乎立刻就收到了 Christian Tellechea 的电子邮件(http://ctan.org/author/tellechea) 指出了这个软件包的很多缺陷。但更重要的是,我的缺陷软件包启发了他编写一个更好的软件包:listofitems
,这个软件包最近才发布(http://ctan.org/pkg/listofitems)
这是一个非常强大且编写良好的包,可以执行输入列表的嵌套解析。在此示例中,我们不使用嵌套,而是使用同时解析两个不同字符的巧妙功能:连字符 ( -
) 或空格 ( )。
\documentclass{article}
\usepackage{listofitems}
\newcommand\foo[1]{%
\setsepchar{-|| }%
\readlist\foobar{#1}%
\ifnum\foobarlen=1\fooA{#1}\else%
\ifnum\foobarlen=3%
\if\foobarsep[1]-%
\fooD{\foobar[1]}\fooE{\foobar[2]\foobarsep[2]\foobar[3]}%
\else
\fooB{\foobar[1]}\fooC{\foobar[2]\foobarsep[2]\foobar[3]}%
\fi
\else%
Invalid argument!
\fi%
\fi%
}
\def\fooA#1{\textbackslash fooA\{#1\}\par}
\def\fooB#1{\textbackslash fooB\{#1\}}
\def\fooC#1{\textbackslash fooC\{#1\}\par}
\def\fooD#1{\textbackslash fooD\{#1\}}
\def\fooE#1{\textbackslash fooE\{#1\}\par}
\begin{document}
\foo{foobar}
\foo{foo bar baz}
\foo{foo bar-baz}
\foo{foo-bar baz}
\foo{foo-bar-baz}
\end{document}
该listofitems
包甚至设置为以纯文本形式工作:
\input listofitems.tex
\def\foo#1{%
\setsepchar{-|| }%
\readlist\foobar{#1}%
\ifnum\foobarlen=1\relax\fooA{#1}\else%
\ifnum\foobarlen=3%
\if\foobarsep[1]-%
\fooD{\foobar[1]}\fooE{\foobar[2]\foobarsep[2]\foobar[3]}%
\else
\fooB{\foobar[1]}\fooC{\foobar[2]\foobarsep[2]\foobar[3]}%
\fi
\else%
Invalid argument!
\fi%
\fi%
}
\def\fooA#1{FooA[#1]\par}
\def\fooB#1{FooB[#1]}
\def\fooC#1{FooC[#1]\par}
\def\fooD#1{FooD[#1]}
\def\fooE#1{FooE[#1]\par}
\foo{foobar}
\foo{foo bar baz}
\foo{foo bar-baz}
\foo{foo-bar baz}
\foo{foo-bar-baz}
\bye
这是一个有趣的示例(使用 LaTeX),展示了嵌套解析。宏*
的形式\readlist
会从每个列表项中删除前导/尾随空格。虽然这里不是这种情况,但可以调用\ignoreemptyitems
或\reademptyitems
(默认)来决定如何处理列表中的空项。
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{listofitems}
\usepackage{amssymb}
\begin{document}
% LEVEL 1 PARSE CHAR: \\
% LEVEL 2 PARSE CHAR: &
% LEVEL 3 PARSE CHARS: + OR -
\setsepchar{\\/&/+||-}
\readlist*\foo{
y &= mx + b\\
E &= mc^2\\
y &= Ax^2 + \mathbb{B}x - \mathbb{C }
}
\showitems*\foo\par
\showitems*\foo[1]\par
\showitems*\foo[2]\par
\showitems*\foo[3]... $\foo[3,2]$\par
1st term: $\foo[3,2,1]$\par
Following separator: $\foosep[3,2,1]$\par
2nd term: $\foo[3,2,2]$\par
Following separator: $\foosep[3,2,2]$\par
3rd term: $\foo[3,2,3]$
\end{document}
答案2
中的实现expl3
;如果有连字符,我们会检查连字符前的文本是否包含空格,否则我们遵循 BC 路线;如果没有连字符,文本将在空格处拆分并遵循 DE 路线。没有空格和连字符将遵循 A 路线。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\foo}{m}
{% call an internal function
\hagen_foo:n { #1 }
}
\cs_new_protected:Nn \hagen_foo:n
{% check whether there is a hyphen first
\seq_set_split:Nnn \l_hagen_foo_seq { - } { #1 }
\int_compare:nTF { \seq_count:N \l_hagen_foo_seq = 1 }
{% no hyphen
\hagen_foo_spaces:n { #1 }
}
{% at least a hyphen
\seq_pop_left:NN \l_hagen_foo_seq \l_hagen_foo_tl
\tl_if_in:NnTF \l_hagen_foo_tl { ~ }
{% the text before the hyphen has a space
\hagen_foo_spaces:n { #1 }
}
{% no space
\hagen_fooD:V \l_hagen_foo_tl
\hagen_fooE:f { \seq_use:Nn \l_hagen_foo_seq { - } }
}
}
}
\cs_new_protected:Nn \hagen_foo_spaces:n
{% check whether there is at least a space
\seq_set_split:Nnn \l_hagen_foo_seq { ~ } { #1 }
\seq_pop_left:NN \l_hagen_foo_seq \l_hagen_foo_tl
\seq_if_empty:NTF \l_hagen_foo_seq
{% no space
\hagen_fooA:V \l_hagen_foo_tl
}
{% at least a space
\hagen_fooB:V \l_hagen_foo_tl
\hagen_fooC:f { \seq_use:Nn \l_hagen_foo_seq { ~ } }
}
}
% here the auxiliary macros should be defined
\cs_new_protected:Nn \hagen_fooA:n {\texttt{|fooA: #1|}}
\cs_new_protected:Nn \hagen_fooB:n {\texttt{|fooB: #1|}}
\cs_new_protected:Nn \hagen_fooC:n {\texttt{|fooC: #1|}}
\cs_new_protected:Nn \hagen_fooD:n {\texttt{|fooD: #1|}}
\cs_new_protected:Nn \hagen_fooE:n {\texttt{|fooE: #1|}}
% and here the required variants
\cs_generate_variant:Nn \hagen_fooA:n { V }
\cs_generate_variant:Nn \hagen_fooB:n { V }
\cs_generate_variant:Nn \hagen_fooC:n { f }
\cs_generate_variant:Nn \hagen_fooD:n { V }
\cs_generate_variant:Nn \hagen_fooE:n { f }
\ExplSyntaxOff
\begin{document}
\foo{foobar}\par
\foo{foo bar baz}\par
\foo{foo bar-baz}\par
\foo{foo-bar baz}\par
\foo{foo-bar-baz}\par
\end{document}
答案3
这是一个基于 LuaLaTeX 的解决方案。它设置了一个名为 的 LaTeX 宏\foo
,该宏又调用一个名为 的 Lua 函数,该HvE
函数检查 的参数\foo
并执行字符串的拆分以及将子字符串分配给\fooA
、\fooB
等。
\documentclass{article}
\usepackage{luacode}
\begin{luacode*}
function HvE ( s )
if string.find ( s , "^%a-%s" ) then
s = string.gsub ( s , "^(%a-)%s(.*)" , "\\fooB{%1}\\fooC{%2}" )
elseif string.find ( s , "^%a-%-" ) then
s = string.gsub ( s , "^(%a-)%-(.*)" , "\\fooD{%1}\\fooE{%2}" )
else
s = "\\fooA{"..s.."}"
end
tex.sprint ( s )
end
\end{luacode*}
\newcommand\foo[1]{\directlua{HvE(\luastring{#1})}}
% set up test versions of \fooA, \fooB, etc
\usepackage{xcolor}
\newcommand\fooA[1]{\textit{\textbf{#1}}}
\newcommand\fooB[1]{\textit{#1}}
\newcommand\fooC[1]{\textcolor{red}{\textbf{#1}}}
\newcommand\fooD[1]{\textcolor{purple}{\textsc{#1}}}
\newcommand\fooE[1]{\textsf{#1}}
\begin{document}
\obeylines % just for this example
\foo{foobar}
\foo{foo bar baz}
\foo{foo bar-baz}
\foo{foo-bar baz}
\foo{foo-bar-baz}
\end{document}