
Я написал следующие команды, которые сначала делят входной текст на части, ;
а затем делают «дроби» из текста, разделенного /
. Моя цель — отобразить генотипы с правильным форматированием.
\documentclass{article}
\usepackage{amsmath}
\usepackage{xstring}
\newcommand\genoLineDist{.2mm}
\newcommand{\genoSplit}[2]{%
$\begin{array}{@{}c@{}}
\text{\protect#1} \\
\noalign{\vskip\genoLineDist}
\hline
\noalign{\vskip\genoLineDist}
\text{\protect#2}
\end{array}$%
}
\newcommand{\geno}[1]{%
\def\remainder{#1}%
\def\splitchar{;}%
\genoHelper
}
\newcommand{\genoHelper}{%
\IfSubStr{\remainder}{\splitchar}{%
\StrBefore{\remainder}{\splitchar}[\firstpart]%
\StrBehind{\remainder}{\splitchar}[\remainder]%
\genoSplitHelper
\ifx\remainder\empty
\else
\ ;\ \genoHelper
\fi
}{%
\def\firstpart{\remainder}%
\genoSplitHelper
}%
}
\newcommand{\genoSplitHelper}{%
\IfSubStr{\firstpart}{/}{%
\StrBefore{\firstpart}{/}[\upperGeno]%
\StrBehind{\firstpart}{/}[\lowerGenoTemp]%
\IfSubStr{\lowerGenoTemp}{\splitchar}{%
\StrBefore{\lowerGenoTemp}{\splitchar}[\lowerGeno]%
\StrBehind{\lowerGenoTemp}{\splitchar}[\remainder]%
}{\def\lowerGeno{\lowerGenoTemp}}%
\genoSplit{\upperGeno}{\lowerGeno}%
}{%
\firstpart\unskip%
}%
}
\begin{document}
%working
\geno{y w ; ap$^{DG3}$ / CyO}
%breaks
%\geno{ y w ; ap\textsuperscript{DG3} / CyO}
\end{document}
Теперь, введя следующую команду в тексте документа
\geno{y w ; ap$^{DG3}$ / CyO}
Мы производим следующий вывод:
Однако при попытке включить какие-либо команды, например \textsuperscript
, или символы, например \Male
или \Female
в любое место входной строки, я сталкиваюсь со следующей ошибкой:
Incomplete \iffalse; all text was ignored after line [line with \geno command].
Моя лучшая догадка заключается в том, что я сталкиваюсь с проблемами с расширением аргумента в одном из операторов if, поэтому я попытался разместить некоторые \protect
s в местах, которые, как я подозревал, могли бы вызвать проблемы, например, когда \remainder
is first defined в \geno
команде и \IfSubStr
to little success. Я также пытался использовать \MakeRobust
команду следующим образом:
%definitions above here
\MakeRobust{\geno}
\MakeRobust{\genoHelper}
\MakeRobust{\genoSplitHelper}
Но, к сожалению, проблема осталась.
Другие слепые выстрелы в темноте включают в себя:
- заменив
\ifx
на\if\relax\detokenize{\remainder}\relax
(та же ошибка) \detokenize
вводим данные в\IfSubStr
(кажется, все они возвращают false, может быть, делаем что-то неправильно?)- Использование различных альтернатив для
\genoSplit
команды (та же ошибка, теперь я уверен, что проблема не в этом) - Переписываем все команды для использования
\newcommand
и\renewcommand
(та же ошибка)
Я также нашелэтот вопрос, но я не уверен, что это та же проблема, с которой я столкнулся, и принятый ответ ( \begingroup\noexpandarg [...] \endgroup
) препятствует работе макроса.
Я в растерянности и был бы очень признателен, если бы мне объяснили, что является причиной этой проблемы.
решение1
xstrings
не любит хрупких команд в своих аргументах.
Вот expl3
реализация: сначала мы разделяем по точкам с запятой; затем каждый элемент передается в качестве аргумента функции, которая создает поддельную дробь, если /
она присутствует.
\documentclass{article}
\usepackage{amsmath}
\newcommand\genoLineDist{.2mm}
\ExplSyntaxOn
\NewDocumentCommand{\geno}{m}
{
\tired_geno:n { #1 }
}
\seq_new:N \l__tired_geno_parts_in_seq
\seq_new:N \l__tired_geno_parts_out_seq
\seq_new:N \l__tired_geno_temp_seq
\cs_new_protected:Nn \tired_geno:n
{
\seq_set_split:Nnn \l__tired_geno_parts_in_seq { ; } { #1 }
\seq_set_map:NNn \l__tired_geno_parts_out_seq \l__tired_geno_parts_in_seq
{
\__tired_geno_split:n { ##1 }
}
\seq_use:Nn \l__tired_geno_parts_out_seq { \ ; \ }
}
\cs_new_protected:Nn \__tired_geno_split:n
{
\seq_set_split:Nnn \l__tired_geno_temp_seq { / } { #1 }
\int_compare:nTF { \seq_count:N \l__tired_geno_temp_seq == 1 }
{
#1 % no /
}
{
\begin{tabular}{@{}c@{}}
\seq_item:Nn \l__tired_geno_temp_seq { 1 } \\
\noalign{\vskip\genoLineDist}
\hline
\noalign{\vskip\genoLineDist}
\seq_item:Nn \l__tired_geno_temp_seq { 2 } \\
\end{tabular}
}
}
\ExplSyntaxOff
\begin{document}
\geno{y w ; ap$^{DG3}$ / CyO}
\geno{ y w ; ap\textsuperscript{DG3} / CyO}
\geno{y/w ; ap$^{DG3}$ / CyO}
\end{document}
решение2
Используя только примитивные команды TeX, вы можете определить свой \geno
макрос следующим образом:
\def\geno#1{\def\genoS{\def\genoS{; }}\genoA #1; \end; }
\def\genoA#1; {\ifx\end#1\else
\genoS
\isinslash#1/\iffalse #1\else \genoB #1/\fi
\expandafter\genoA
\fi
}
\def\genoB #1/#2/{$\displaystyle{\hbox{#1\unskip}\over\hbox{\ignorespaces#2\unskip}}$ }
\def\isinslash #1/#2\iffalse{\ifx/#2/}
\geno{y w ; ap$^{DG3}$ / CyO ; next ; A/B}
решение3
Этого может быть достаточно, используя listofitems
синтаксический анализ. ОТРЕДАКТИРОВАНО для обработки более общего синтаксиса, указанного автором в комментарии.
\documentclass{article}
\usepackage{amsmath}
\usepackage{listofitems}
\newcommand\geno[1]{%
\setsepchar[&]{;&/}%
\readlist*\myterm{#1}%
\foreachitem\z\in\myterm[]{%
\ifnum\zcnt=1 \else \ ;\ \fi
\ifnum\listlen\myterm[\zcnt]=1
\myterm[\zcnt]%
\else
$\displaystyle
\frac{\text{\myterm[\zcnt,1]}}{\text{\myterm[\zcnt,2]}}$%
\fi
}%
}
\begin{document}
\geno{y w ; ap$^{DG3}$ / CyO}
\bigskip
\geno{ y w ; ap\textsuperscript{DG3} / CyO}
\bigskip
\geno{ y w ; ap\textsuperscript{DG3} / CyO ;
bp\textsuperscript{DG0} / CzO ; z x}
\end{document}