另一个话题,我得到了以下命令作为答案:它采用两个字母的字体形状(sc
,it
...)或完整版本(scshape
,itshape
...)来设置相应的字体形状。
\documentclass{article}
\newcommand*\setfontshape[1]{\setfontshapeA #1shape\relax}
\def\setfontshapeA#1shape#2\relax{\csname #1shape\endcsname}
\begin{document}
{\setfontshape{sc} hello}
{\setfontshape{scshape} hello}
\end{document}
问题是,当传递另一个宏时,命令会中断
\documentclass{article}
\newcommand*\setfontshape[1]{\setfontshapeA #1shape\relax}
\def\setfontshapeA#1shape#2\relax{\csname #1shape\endcsname}
\newcommand{\myshapeshort}{sc}
\newcommand{\myshapefull}{scshape}
\begin{document}
{\setfontshape{\myshapeshort} hello} % Works
{\setfontshape{\myshapefull} hello} % Does not work
\end{document}
有没有办法使命令更加健壮,并使其在任何情况下都能工作(甚至是命令、调用命令、调用命令……以及递归)?
答案1
问题:-)
在于扩展,正如您自己所标记的那样。当您将宏传递给\setfontshape
\setfontshape{\foo}
扩展为
\setfontshapeA\foo shape\relax
\setfontshapeA
现在的扩展不是扩展宏\foo
本身,但只返回
\csname\foo shape\endcsname
如果\foo
仅包含sc
则有效,否则无效。
您必须延迟 的扩展\setfontshapeA
。只要您传递给 的参数\setfontshape
最多只有一个宏,那么一个简单的
\newcommand*\setfontshape[1]{\expandafter\setfontshapeA #1shape\relax}
可能会奏效。但是,如果你编造出像
\def\foo{scsh}
\def\baz{ape}
\setfontshape{\foo\baz}
那么你需要先充分展开论点。你可以这样做
\newcommand*\setfontshape[1]{\expandafter\setfontshapeA\expanded{#1}shape\relax}
或者
\newcommand*\setfontshape[1]{\edef\next{#1shape}\expandafter\setfontshapeA\next\relax}
前者需要相对较新的 pdfTeX(2018),而后者是纯 Knuth-TeX。
答案2
扩展问题。我的解决方案是定义一个合适的变体。
\documentclass{article}
\usepackage{lmodern}
\ExplSyntaxOn
\NewDocumentCommand{\setfontshape}{m}
{
\IfBlankF{#1}
{
\vincent_shape:e { #1 }
}
}
\cs_new_protected:Nn \vincent_shape:n
{
\use:c { #1 \int_compare:nT { \str_count:n { #1 } < 5 } { shape } }
}
\cs_generate_variant:Nn \vincent_shape:n { e }
\ExplSyntaxOff
\newcommand{\myshapeshort}{sc}
\newcommand{\myshapefull}{scshape}
\begin{document}
{\setfontshape{sc}AbcDef}
{\setfontshape{scshape}AbcDef}
{\setfontshape{it}AbcDef}
{\setfontshape{itshape}AbcDef}
{\setfontshape{sl}AbcDef}
{\setfontshape{slshape}AbcDef}
{\setfontshape{sc}\setfontshape{sl}AbcDef}
{\setfontshape{\myshapeshort} hello} % Works
{\setfontshape{\myshapefull} hello} % Works
\end{document}
答案3
如果你喜欢 Knuth-TeX 引擎的代码,你可以嵌套\csname
-expansion 以通过 inner 扩展内容,\csname..\endcsname
然后再通过 outer 进行字符串化和删除“形状” \csname..\endcsname
:
\documentclass{article}
\csname @ifdefinable\endcsname\MyRemoveXYAndshapeAndBeyond{%
\edef\MyRemoveXYAndshapeAndBeyond{%
\long\def\noexpand\MyRemoveXYAndshapeAndBeyond
##1\string X\string Y##2\string s\string
h\string a\string p\string e##3\relax{##2}%
}%
\MyRemoveXYAndshapeAndBeyond
}
\newcommand\setfontshape[1]{%
\csname
\expandafter\expandafter
\expandafter \MyRemoveXYAndshapeAndBeyond
\expandafter\string
\csname XY#1shape\endcsname\relax
shape%
\endcsname
}
\newcommand{\myshapeshort}{sc}
\newcommand{\myshapefull}{scshape}
\begin{document}
{\setfontshape{sc} hello}
{\setfontshape{scshape} hello}
{\setfontshape{\myshapeshort} hello} % Works
{\setfontshape{\myshapefull} hello} % Works
{\setfontshape{\myshapefull this is a secret message} hello} % Works
\end{document}
如果正在使用 TeX 引擎\expanded
并且\unexpanded
可用,并且您希望通过一些卑鄙的用户输入来防止奇怪的“缺少 \endcsname”错误,您可以执行以下操作:
\documentclass{article}
\expanded{%
\long\def\noexpand\MyRemoveshapeAndBeyond
#1\string s\string h\string a\string p\string e%
}%
#2\relax{#1}
\newcommand\setfontshape[1]{%
\csname
\expandafter\MyRemoveshapeAndBeyond
\detokenize\expanded{{#1shape}}\relax
shape%
\endcsname
}
\newcommand{\myshapeshort}{sc}
\newcommand{\myshapefull}{scshape}
\begin{document}
{\setfontshape{sc} hello}
{\setfontshape{scshape} hello}
{\setfontshape{\myshapeshort} hello} % Works
{\setfontshape{\myshapefull} hello} % Works
{\setfontshape{\myshapefull this is a secret message} hello} % Works
\end{document}
你提出了这个问题:
有没有办法使命令更加健壮,并使其在任何情况下都能工作(甚至是命令、调用命令、调用命令……以及递归)?
调用命令意味着尝试成功执行算法。 在您的情况中,“成功执行算法”在其他事情之下意味着所讨论的算法
- 只能传递适合形成命令名的标记。此要求意味着算法的执行只能由扩展来驱动。
- 可能无法终止,因为已经创建了需要中止并提供错误消息或类似信息的情况。
- 应该在某个时候终止。
我认为很难实现对任意宏参数的检查,即基于宏的机制(最初具有一组特定的宏参数)是否会产生满足这些要求的算法,如果不是,则以某种方式化解/消除这种情况。