请考虑以下 MWE:
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{etoolbox}
\newcommand*{\convertlang}[1]{%
\ifstrequal{#1}{es}{spanish}{%
\ifstrequal{#1}{lat}{latin}{%
\ifstrequal{#1}{eng}{english}{%
\ifstrequal{#1}{deu}{german}{%
\ifstrequal{#1}{enm}{spanish}{% else
% use English as foreign default
english}}}}}}%
\newcommand*{\langtest}[1]{%
\def\temp{\convertlang{#1}}%
\ifstrequal{\temp}{spanish}{True}{False}%
}
\begin{document}
Testing: \convertlang{es} % yields: spanish
\langtest{es} % should yield: True
\end{document}
该命令\convertlang
接受 ISO 8859 语言代码(es
、eng
等)并将其转换为 babel 语言名称(spanish
、english
等)。这很有效。
问题在于\langtest
,它将字符串 ( ) 与使用spanish
转换其参数的结果 ( ) 进行比较。#1
\convertlang
\convertlang
那里的and/or的扩展肯定有问题#1
,但我不了解 TeX 宏扩展的基本原理。
我尝试这样做:
\newcommand*{\langtest}[1]{%
\edef\temp{\convertlang{#1}}%
\ifstrequal{\temp}{spanish}{True}{False}%
}
期望\temp
得到其参数的扩展值。但这也行不通。
我怎样才能解决这个问题?
答案1
您可以使用可扩展和扩展字符串相等性测试(出于\pdfstrcmp
可移植性的考虑,请参见\pdf@strcmp
pdftexcmds
LaTeX 中是否有类似“\ifnum”的“if”命令?)。该命令扩展了它的测试参数,并且本身也是可扩展的。(后者总是好的,前者则不然总是方便 [参见https://tex.stackexchange.com/q/230878/35864],但这正是我们想要的。)
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{etoolbox}
\usepackage{pdftexcmds}
\makeatletter
\newcommand*{\IfStrEqualTF}[2]{%
\ifnum\pdf@strcmp{#1}{#2}=\z@
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi}
\makeatother
\newcommand*{\convertlang}[1]{%
\IfStrEqualTF{#1}{es}
{spanish}
{\IfStrEqualTF{#1}{lat}
{latin}
{\IfStrEqualTF{#1}{eng}
{english}
{\IfStrEqualTF{#1}{deu}
{german}
{\IfStrEqualTF{#1}{enm}
{spanish}
{english}}}}}}
\newcommand*{\langtest}[1]{%
\IfStrEqualTF{\convertlang{#1}}{spanish}
{True}
{False}}
\begin{document}
Testing: \convertlang{es} % yields: spanish
\langtest{es} % should yield: True
\langtest{en} % should yield: False
\end{document}
关于 MWE 的更多细节以及它为什么没有达到预期的效果。
第一的其中,etoolbox
的\ifstrequal
定义是\newrobustcmd
。因此它是健壮的,不可扩展的。这意味着
\edef\temp{\convertlang{#1}}
实际上并没有像我们希望的那样保存较长的语言名称\temp
。它只是保存了一系列\ifstrequal
测试。这意味着\temp
不包含简单的字符串。
\edef\temp{\convertlang{es}}%
\show\temp
给出
> \temp=macro: ->\ifstrequal {es}{es}{spanish}{\ifstrequal {es}{lat}{latin}{\ifstrequal {es}{eng}{english}{\ifstrequal {es}{deu}{german}{\ifstrequal {es}{enm}{spanish}{english}}}}}.
您需要一个可扩展的字符串相等性测试,以允许\convertlang{#1}
扩展到 中的语言名称\edef
。
\pdf@strcmp
在这里很有用,因为它的字符串比较是可扩展,这意味着通过此相等性测试定义的命令可以扩展为 中的比较结果\edef
。
这第二问题是它\ifstrequal
没有扩展其参数,因此即使\temp
只包含一个字符串,测试也无法按预期工作
\def\temp{spanish}%
\ifstrequal{\temp}{spanish}{True}{False}%
仍然给出 'False',因为\temp
是不是等于的字符串spanish
,它扩展到等于的字符串spanish
,这是一个微小但重要的差异。
有几种可能的方法可以解决这个问题。\temp
这取决于你能保证什么,足以说
\expandafter\ifstrequal\expandafter{\temp}{spanish}{True}{False}%
或者
\ifdefstring{\temp}{spanish}{True}{False}%
如果您知道\temp
在一个扩展步骤中将其扩展为字符串。如果需要更多步骤或想要完全扩展,则需要其他技巧。
\pdf@strcmp
在这里很有帮助,因为它只是完全扩展了它的参数。这意味着只要\temp
不包含在扩展上下文中爆炸的任何内容,就可以比较其“最终扩展字符串值”。您不必担心先对其进行扩展以进行测试。
答案2
expl3
我会使用更简单的界面\str_case:nnF
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\convertlang}{m}
{
\nvaughan_convertlang:n { #1 }
}
\cs_new:Nn \nvaughan_convertlang:n
{
\str_case:nnF { #1 }
{
{es}{spanish}
{lat}{latin}
{eng}{english}
{deu}{german}
{enm}{spanish}
}
{english}
}
\NewExpandableDocumentCommand{\langtest}{m}
{
\str_if_eq:eeTF { \nvaughan_convertlang:n { #1 } } { spanish } { True } { False }
}
\ExplSyntaxOff
\begin{document}
Testing: \convertlang{es} % yields: spanish
\langtest{es} should yield: True
\langtest{lat} should yield: False
\end{document}
答案3
我不会使用长嵌套的 if 测试。这很难扩展。
\documentclass{article}
\ExplSyntaxOn
\tl_const:Nn \c__nvaughan_convert_es_tl{spanish}
\tl_const:Nn \c__nvaughan_convert_lat_tl{latin}
\tl_const:Nn \c__nvaughan_convert_eng_tl{english}
\tl_const:Nn \c__nvaughan_convert_deu_tl{german}
\tl_const:Nn \c__nvaughan_convert_enm_tl{spanish}
\newcommand*{\convertlang}[1]
{
\tl_if_exist:cTF {c__nvaughan_convert_#1_tl}
{ \tl_use:c {c__nvaughan_convert_#1_tl} }
{ english}
}
\newcommand*{\langtest}[1]{%
\str_if_eq:eeTF {\convertlang{#1}}{spanish}
{True}{False}}
\ExplSyntaxOff
\begin{document}
Testing: \convertlang{es} % yields: spanish
\convertlang{lat} \convertlang{blub}
\langtest{es} % should yield: True
\end{document}