我一直在编写一个 LaTeX 递归流处理器,将一组常用命令的简写版本转换为更长的形式。下面的代码代表了我在意识到 xstrings 不允许嵌套宏之前所做的工作。
该程序应该执行以下操作:
- 检查输入的长度是否为 0。
- 否则它会检查第一组中是否有任何命令(目前只有旋转)
- 否则,它会检查第二组中是否有任何命令(其中 flip 是)
- 否则它只返回符号
如果它找到一个符号,它会嵌套并使用输入流中的剩余字符进行递归调用,否则它只是将递归调用附加到当前字符。
我想保留程序的递归性质,因为这是最合乎逻辑的,因为有可能出现嵌套的情况
\newcommand{\processSymbol}[1]{
\IfEq{0}{\StrLen{#1}}{}{\CheckRotate{\firstChar{#1}}{\restChars{#1}}}
}
\newcommand{\AECheckRotate}[2]{
\begin{switch}{#1}
\case{c}{\AEccwRotate{\processSymbol{#2}}}
\AECheckFlip{#1}{#2}
\end{switch}
}
\newcommand{\AECheckFlip}[2]{
\begin{switch}
\case{v}{\flipv{\processSymbol{#2}}}
#1\processSymbol{#2}
\end{switch}
}
尽管命令集是任意的,但我编写的示例代码的示例输入将是这样的:
vccb
在我的完整代码中应该返回如下内容:
P
因为它会旋转、旋转并垂直翻转字母 b。
我正在尝试找出另一种方法以这种方式进行嵌套字符串解析。我感觉用 xstrings 无法做到这一点。如果我需要切换到使用 LuaTex,那就这样吧。在学习 Lua 之前,我只是想在 LaTeX 中做到这一点
答案1
在这里,我定义了水平反射、垂直反射和顺时针旋转的宏。我首先展示了如何将它们嵌套为\myvreflect{\myrotate{\myrotate{b}}}
。然后我展示了如何使用宏\mytransform
来简化操作,例如vccb
以递归方式完成相同的操作。
(请注意,在我的 MWE 中,“垂直”被定义为“沿垂直轴翻转”而不是“沿水平轴垂直翻转”。我这样做是为了匹配 OP 的命名法。对于“水平”,其含义也是“沿水平轴翻转”。)
已编辑以处理空参数。已重新编辑以显示如何对大于单个字符的参数进行递归操作。
\documentclass{article}
\usepackage{graphicx}
\def\myhreflect#1{\scalebox{1}[-1]{#1}}
\def\myvreflect#1{\scalebox{-1}[1]{#1}}
\def\myrotate#1{\rotatebox{90}{#1}}
\newcommand\mytransform[1]{\mytransformhelp#1\relax\relax}
\def\mytransformhelp#1#2\relax{%
\if\relax#2\relax#1\else%
\if v#1\myvreflect{\mytransformhelp#2\relax}\else%
\if h#1\myhreflect{\mytransformhelp#2\relax}\else%
\if c#1\myrotate{\mytransformhelp#2\relax}%
\fi%
\fi%
\fi%
\fi%
}
\begin{document}
b \myvreflect{\myrotate{\myrotate{b}}}
\mytransform{vccb}\quad
\mytransform{vcb}\quad
\mytransform{hccb}\quad
\mytransform{hcb}
\def\x{test}
\mytransform{vcc\x}
\end{document}
答案2
这是一个使用 xstring 的方法:
\documentclass{article}
\usepackage{xstring,graphicx}
\def\nestcommand#1#2{%
\ifx#1\relax \StrSubstitute#1\relax{#2\relax}[#1]%
\else \StrSubstitute#1\relax{#2{\relax}}[#1]%
\fi
}
\def\processSymbol#1{%
\def\processcommand{\relax}%
\edef\tempcommandset{,\unexpanded\expandafter{\commandcharlist},}%
\def\remaining{#1}%
\saveexpandmode\expandarg\saveexploremode\exploregroups
\processSymbolRecurse
}
\def\processSymbolRecurse{%
\unless\ifx\remaining\empty
\StrSplit\remaining 1\firstchar\tempremaining
\IfSubStr\tempcommandset{\expandafter,\firstchar=}
{\let\remaining=\tempremaining
\StrBehind\tempcommandset{\expandafter,\firstchar=}[\currentcommand]%
\StrBefore\currentcommand,[\currentcommand]%
\nestcommand\processcommand\currentcommand
\expandafter\processSymbolRecurse
}
{\StrSubstitute\processcommand\relax\remaining[\processcommand]%
\restoreexpandmode\restoreexploremode
\expandafter\processcommand
}%
\fi
}
\begin{document}
\def\commandcharlist{r=\rotatebox{90},h=\scalebox{1}[-1],v=\scalebox{-1}[1],f=\fbox,b=\bfseries}%
\processSymbol{rhfy}% same as \rotate{90}{\scalebox{1}[-1]{\fbox{y}}}
\def\test{Test}
\processSymbol{fb\test} or \processSymbol{fbTest}
\def\test{b}
\processSymbol{hrr\test}
\end{document}
答案3
这是一个expl3
版本。我不确定你想要实现什么,所以这只是一次尝试。
需要打印而不是解释的符号必须用双括号括起来。
\documentclass{article}
\usepackage{xparse}
\usepackage{graphicx}
\ExplSyntaxOn
\NewDocumentCommand\processSymbol{m}
{
\fallon_process_symbol:n { #1 }
}
\tl_new:N \l__fallon_head_tl
\tl_new:N \l__fallon_tail_tl
\tl_const:Nn \c_fallon_bgroup_tl { \if_true: { \else: } \fi: }
\tl_const:Nn \c_fallon_egroup_tl { \if_false: { \else: } \fi: }
\cs_new_protected:Npn \fallon_process_symbol:n #1
{
\tl_clear:N \l__fallon_head_tl
\tl_clear:N \l__fallon_tail_tl
\tl_map_inline:nn { #1 }
{
\str_case:nnF { ##1 }
{
{v}{ \__fallon_addto_head:N \fhreflect }
{h}{ \__fallon_addto_head:N \fvreflect }
{c}{ \__fallon_addto_head:N \frotate }
}
{
\fallon_output:n { ##1 }
}
}
}
\cs_new_protected:Npn \__fallon_addto_head:N #1
{
\tl_put_right:Nn \l__fallon_head_tl { #1 \c_fallon_bgroup_tl }
\tl_put_left:Nn \l__fallon_tail_tl { \c_fallon_egroup_tl }
}
\cs_new_protected:Npn \fallon_output:n #1
{
\tl_put_right:Nn \l__fallon_head_tl { \exp_not:n { #1 } }
\tl_put_right:NV \l__fallon_head_tl \l__fallon_tail_tl
\tl_set:Nx \l__fallon_head_tl { \l__fallon_head_tl }
\tl_use:N \l__fallon_head_tl
\tl_clear:N \l__fallon_head_tl
\tl_clear:N \l__fallon_tail_tl
}
\ExplSyntaxOff
\NewDocumentCommand\fhreflect{m}{\scalebox{1}[-1]{#1}}
\NewDocumentCommand\fvreflect{m}{\scalebox{-1}[1]{#1}}
\NewDocumentCommand\frotate{m}{\rotatebox{90}{#1}}
\begin{document}
\processSymbol{vccb}\quad
\processSymbol{vcb}\quad
\processSymbol{hccb}\quad
\processSymbol{hc{{v}}}
\def\x{test}
\processSymbol{vcc\x}
\processSymbol{vccbvccy}
\end{document}
各种命令字母被转换成一个命令,该命令与隐式左括号一起附加到标记列表中,并且匹配的隐式左括号被添加到另一个标记列表中。当发现命令字母中没有的符号时,标记列表被连接起来,完全展开并传递;该过程重新开始。
答案4
我认为除了轮换之外你不需要任何其他包,但我可能误解了
但我认为你没有具体说明 rotate 应该做什么。(我\rotatebox{90}
在这里假设。
\documentclass{article}
\usepackage{graphicx}
\def\zz#1{\zzz#1\zzz}
\def\zzz#1{%
\expandafter\ifx\csname\string#1!\endcsname\relax
#1%
\expandafter\zzz
\else
\csname\string#1!\expandafter\endcsname
\fi}
\expandafter\def\csname\string\zzz!\endcsname{}
\expandafter\def\csname c!\endcsname#1\zzz{%
\rotatebox{90}{\zz{#1}}}
\expandafter\def\csname v!\endcsname#1\zzz{%
\scalebox{1}[-1]{\zz{#1}}}
\begin{document}
\zz{vccb}
\end{document}