![Рекурсивный анализ строк](https://rvso.com/image/305748/%D0%A0%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD%D1%8B%D0%B9%20%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%20%D1%81%D1%82%D1%80%D0%BE%D0%BA.png)
Я работаю над написанием рекурсивного потокового процессора в latex, который преобразует сокращенную версию набора часто используемых команд в гораздо более длинную форму. Код ниже представляет то, что я сделал до сих пор, прежде чем понял, что xstrings не позволяет вам вкладывать макросы.
Программа должна выполнять следующие действия:
- Проверяет, имеет ли входная информация нулевую длину.
- в противном случае он проверяет, есть ли какие-либо команды в первом наборе (сейчас там только rotate)
- в противном случае он проверяет, есть ли какие-либо команды во втором наборе (где находится 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, то пусть будет так. просто пытаюсь сделать это в LaTeX, прежде чем мне нужно будет изучить Lua
решение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}