![Análisis recursivo de cadenas](https://rvso.com/image/305748/An%C3%A1lisis%20recursivo%20de%20cadenas.png)
He estado trabajando en escribir un procesador de flujo recursivo en látex que convierta una versión abreviada de un conjunto de comandos muy utilizados en una forma mucho más larga. El siguiente código representa lo que había hecho hasta ahora antes de darme cuenta de que xstrings no puede permitirle anidar macros.
Se supone que el programa debe hacer lo siguiente:
- Comprueba si la entrada tiene una longitud de 0.
- de lo contrario, verifica si hay algún comando en el primer conjunto (en este momento solo está rotar)
- de lo contrario, comprueba si hay algún comando en el segundo conjunto (donde está flip)
- de lo contrario simplemente devuelve el símbolo
si encuentra un símbolo, lo anida y lo llama recursivamente con los caracteres restantes en el flujo de entrada; de lo contrario, simplemente agrega la llamada recursiva al carácter actual.
Quiero mantener la naturaleza recursiva del programa porque tiene el sentido más lógico porque existe la posibilidad de que las cosas estén anidadas.
\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}
}
aunque el conjunto de comandos es arbitrario, una entrada de muestra para el código de muestra que escribí sería algo como:
vccb
que en mi código completo debería devolver algo que se verá así:
P
porque rotaría, rotaría y voltearía verticalmente la letra b.
Estoy tratando de encontrar otra forma de realizar el análisis de cadenas anidadas de esta manera. y tengo la sensación de que no se puede hacer con xstrings. Si necesito cambiar a LuaTex, que así sea. Solo intento hacer esto en LaTeX antes de necesitar aprender Lua.
Respuesta1
Aquí, defino macros para reflexión horizontal, reflexión vertical y rotación en el sentido de las agujas del reloj. Primero muestro cómo se pueden anidar como \myvreflect{\myrotate{\myrotate{b}}}
. Luego muestro cómo \mytransform
se puede utilizar la macro para taquigrafiar, por ejemplo, vccb
para lograr la misma cosa de forma recursiva.
(Tenga en cuenta que en mi MWE, "vertical" se define como "voltear a través de un eje vertical" y no "voltear verticalmente a través de un eje horizontal". Hago esto para poder coincidir con la nomenclatura del OP. Lo mismo ocurre con "horizontal", es decir "voltear sobre un eje horizontal".)
EDITADO para manejar argumentos nulos. REEDITADO para mostrar cómo operar de forma recursiva con argumentos de más de un solo carácter.
\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}
Respuesta2
Aquí hay un método con 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}
Respuesta3
Aquí tienes una expl3
versión. No estoy seguro de lo que quieres lograr, así que esto es sólo un intento.
Un símbolo que deba imprimirse en lugar de interpretarse debe encerrarse entre llaves dobles.
\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}
Las distintas letras de comando se transforman en un comando que se agrega a una lista de tokens junto con una llave de apertura implícita y se agrega una llave de apertura implícita coincidente en otra lista de tokens. Cuando se encuentra un símbolo que no está entre las letras de comando, las listas de tokens se unen, se expanden completamente y se entregan; el proceso se inicia de nuevo.
Respuesta4
No creo que necesites ningún paquete más que para la rotación, pero es posible que haya entendido mal cuando entiendo
Pero no creo que hayas especificado qué se suponía que debía hacer exactamente la rotación. (Asumí \rotatebox{90}
aquí.
\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}