LXF158:LaTeX: Массивы макрокоманд
|
|
|
LaTeX Автоматизируем рутинные задачи посредством макрокоманд
LaTeX: Массивы макрокоманд
Дмитрий Цымай и Станислав Куценко предлагают разрабатывать тестовые задания для студентов средствами LaTeX, применив их для подготовки текстов.
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
LaTeX – популярный набор макрорасширений системы компьютерной верстки TeX, облегчающий подготовку сложных документов. Применение макропакетов позволяет автоматизировать задачи набора текста и подготовки статей. LaTeX расширяем также за счет написания собственных макрокоманд – например, при помощи встроенной в Tex команды \def, описанной в [1]. Чтобы случайно не переопределить существующую команду, можно пользоваться встроенными в LaTeX командами \newcommand для определения новой команды и \renewcommand для явного переопределения существующей, описанными в [2].
Все это удобно до тех пор, пока не потребуется определить достаточно много новых макрокоманд. Такая задача встала при разработке тестовых заданий для студентов. Одни и те же вопросы могут повторяться в разных тестовых заданиях, и разумно определить для каждого из них макрокоманду: это сократит время и на верстку заданий, и при правке вопросов. Но сложность в том, что каждая макрокоманда требует уникального имени, а цифры в именах использовать нельзя; необходимость держать в памяти множество новых команд сводит к нулю все преимущества их повторного использования. При верстке тестов необходимо иметь способ вставить фрагмент (отдельный вопрос) в произвольное задание, не вспоминая при этом имя командной последовательности. В [1] описан вариант реализации списочных макрокоманд. Данный подход не совсем удобен: он предполагает сохранение всех фрагментов в определении одной списочной макрокоманды.
Одно из возможных решений – массив макрокоманд. Достаточно определить одно имя массива для фрагментов текста, разметить каждый вопрос (начало и конец) определенными ограничивающими командами и в дальнейшем обращаться к данным фрагментам по номерам, в соответствии с порядком их определения в тексте. Присвоение имен новым командам перекладывается на компьютер, т. е. происходит прозрачно для пользователя.
Основные команды для массивов
LaTeX не имеет своих средств для работы с массивами макрокоманд. Встроенные средства программирования [3] предлагают:
- объявление массива с произвольным именем;
- добавление в массив произвольных элементов;
- перебор всех или части элементов массива в цикле с выполнением над элементами операций форматирования.
Разберем особенности реализации. Раз имя макрокоманды не должно включать арабских цифр, заменим их на римские, отображаемые латинскими буквами. Тогда серия команд \qvery1, \qvery2, \qvery3... запишется \qveryi, \qveryii, \qveryiii... . Каждое имя будет уникальным, но строка перед римскими цифрами не должна совпадать с существующими командами LaTeX.
Команда \newArray{<имя массива>} определяет новый массив, и автоматически создает счетчик массива с именем arraycount<имя определяемого массива> и начальным значением 1. Параметр команды – имя нового массива, подчиняющееся тем же правилам, что и имена счетчиков и макрокоманд. Определение команды –
\def\newArray#1{
\newcounter{arraycount#1}
\setcounter{arraycount#1}{1}
}
Для объявления нового массива необходимо записать:
\newArray{<имя массива>}
Команда \AddItem{<имя массива>}{<элемент>} позволяет добавлять элементы в объявленный массив. Каждый элемент будет храниться в новой команде, с именем array<имя массива><значение счетчика римскими цифрами>. Так обеспечивается уникальность имени каждого элемента. Например, для массива с именем qvery имена макрокоманд для 1-го, 2-го и 3-го элементов будут, соответственно, arrayqveryi, arrayqveryii и arrayqveryiii. При выполнении команды счетчик, связанный с массивом, увеличивается на единицу. Команда имеет следующее определение:
\long\def\AddItem#1#2{%
\expandafter\gdef\csname array#1\roman{arraycount#1}\endcsname{#2}
\global\addtocounter{arraycount#1}{1}
}
Рассмотрим элементы Tex в составе определения. Команда
\csname... \endcsname
задает новую командную последовательность без содержания; позже ее можно переопределить. Команда \expandafter велит TeX’у раскрыть последовательность, определяющую имя новой макрокоманды, а затем определить ее глобально при помощи \gdef. Команда \roman{arraycount#1} выводит значение счетчика, связанного с массивом римскими цифрами. Общий синтаксис команды – \roman{<имя счетчика>}.
Элемента массива по его номеру извлекается командой
\GetItem{<имя массива>}{<элемент>}
имеющая два параметра: имя массива и номер элемента.
\long\def\GetItem#1#2{%
\setcounter{temparraycount}{#2}
\ifnum\arabic{temparraycount}<\arabic{arraycount#1}
\csname array#1\roman{temparraycount}\endcsname
\fi
}
Здесь используется вспомогательный счетчик temparraycount, определенный ранее. Условный оператор \ifnum<Команды>\. позволяет выполнять команду, если номер вызываемого элемента не выходит за границы массива. Выполняется сравнение значений счетчиков temparraycount и arraycount<имя массива>, представленные арабскими цифрами, командой \arabic{<имя счетчика>}.
Число элементов массива на 1 меньше значения счетчика arraycount#1. Определение для команды \ArrayCount{<имя массива>} получения числа элементов массива –
\def\ArrayCount#1{
\setcounter{temparraycount}{\arabic{arraycount#1}}
\global\addtocounter{temparraycount}{-1}
\arabic{temparraycount}
\global\addtocounter{temparraycount}{1}
}
Также необходимы команды перечисления всех элементов массива. Их две. Одна является аналогом команды foreach... in.... Она принимает два параметра: имя массива и команду, которую надо исполнить над каждым элементом при его выводе.
\long\def\foreachArray#1#2{%
\setcounter{temparraycount}{1}
\loop\ifnum\arabic{temparraycount}<\arabic{arraycount#1}
- 2{\GetItem{#1}{\arabic{temparraycount}}}
\global\addtocounter{temparraycount}{1}
\repeat
}
Подобна ей команда-аналог цикла for... from... to. У нее 4 параметра: имя массива, номера первого и последнего выводимого элемента и операция, исполняемая над элементами при выводе.
\long\def\ForFromToArray#1#2#3#4{%
\setcounter{temparraycount}{#2}
\setcounter{temparraycounta}{#3}
\ifnum\arabic{temparraycounta}<\arabic{arraycount#1}
\addtocounter{temparraycounta}{1}
\else
\setcounter{temparraycounta}{\arabic{arraycount#1}}
\fi
\loop\ifnum\arabic{temparraycount}<\arabic{temparraycounta}
- 4{\GetItem{#1}{\arabic{temparraycount}}}
\global\addtocounter{temparraycount}{1}
\repeat
}
В двух последних определениях команда \loop\if <условие><тело цикла>... \repeat позволяет использовать цикл. Тело цикла выполняется, пока истинно условие после \if.
Массивы команд в действии
Рассмотрим примеры применения предложенных команд. Для этого в преамбуле документа подключим файл с определениями:
\input{texarray}
Определяем два массива: mytestq и mytestw.
\newArray{mytestq} \newArray{mytestw}
Добавляем элементы в оба массива.
\AddItem{mytestq}{фрагмент 1}
\AddItem{mytestw}{фрагмент 2}
\AddItem{mytestq}{фрагмент 3} \
AddItem{mytestw}{фрагмент 4}
\AddItem{mytestw}{фрагмент 5}
\AddItem{mytestw}{фрагмент 6}
\AddItem{mytestw}{фрагмент 7}
Выводим второй элемент массива mytestq: фрагмент 3.
\GetItem{mytestq}{2}
Количество элементов в массиве mytestq равно 2.
\ArrayCount{mytestq}
Количество элементов в массиве mytestw равно 5.
\ArrayCount{mytestw}
Перечисляем все элементы массива mytestw:
\foreachArray{mytestw}{}
Результат: фрагмент 2 фрагмент 4 фрагмент 5 фрагмент 6 фрагмент 7
Добавляем ещеодин элемент в массив mytestq:
\AddItem{mytestq}{фрагмент 8}
Перечисляем все элементы массива mytestq
\foreachArray{mytestq}{}
Результат: фрагмент 1 фрагмент 3 фрагмент 8
Перечислим все элементы массива mytestq с выполнением операции, заданной вторым параметром, над всеми элементами массива. Для этого определим команду вывода текста жирным курсивом –
\def\testcommand#1{\textbf{\textit{#1}}}
и применим ее в вызове функции перебора элементов массива.
\foreachArray{mytestq}{\testcommand}
Результат: фрагмент 1 фрагмент 3 фрагмент 8
Перечислим элементы массива mytestw со 2-го по 4-й.
\ForFromToArray{mytestw}{2}{4}{}
Результат: фрагмент 4 фрагмент 5 фрагмент 6
Выведем элементы массива mytestw со 2-го по 4-й с выполнением операции над каждым элементом массива.
\ForFromToArray{mytestw}{2}{4}{\testcommand}
Результат: фрагмент 4 фрагмент 5 фрагмент 6
Пробуем создать тесты
Приведенные ранее команды являются достаточно общими, что позволяет использовать их в составе других команд. Рассмотрим одно из их практических применений для верстки тестовых заданий.
- Счетчики и вспомогательные определения
Для работы с макрокомандами определим несколько счетчиков:
- BiletNumber Счетчик номер билета
- VoprosNumber Счетчик для вопросов
- VarOtvNumber Счетчик вариантов ответов
Установим указанные счетчики равными 1 при помощи команд
\newcounter{BiletNumber}
\setcounter{BiletNumber}{1}
\newcounter{VoprosNumber}
\setcounter{VoprosNumber}{1}
\newcounter{VarOtvNumber}
\setcounter{VarOtvNumber}{1}
- Команды для разметки исходных вопросов
Для работы с тестами мы используем созданные нами команды работы с массивами. Определим новый массив:
\newArray{qvery}
Определим команду для обозначения вопроса, добавляющую текстовый фрагмент в массив qvery, основанную на команде \AddItem:
\long\def\Qvery #1\endQvery{%
\AddItem{qvery}{#1}
}
К вопросам, обозначенным при помощи \Qvery #1\endQvery, можно обратиться по номеру, соответствующему их появлению в тексте, при помощи другой команды – \printQ{<номер вопроса>}, основанной на использовании команды \GetItem{qvery}{<номер вопроса>}.
\long\def\printQ#1{%
\setcounter{VarOtvNumber}{1}
\par\noindent\arabic{VoprosNumber}. % \
GetItem{qvery}{#1}
\addtocounter{VoprosNumber}{1}%
}
В результате печатается элемент массива qvery с номером, коорый параметром. Пример выполнения команды показан на рис. 1.
Вариант ответа обозначается командой
\long\def\VarQveryOtv{%
\par\indent\ \ \arabic{VarOtvNumber}) %
\addtocounter{VarOtvNumber}{1}%
}
Команды, определяющие вид печатного документа
Эти команды имеют вспомогательное значение. Они могут быть использованы при необходимости.
Значение переменной printotv определяет, помечать ли в конечном документе правильные ответы, если используются команды для обозначения правильных вариантов ответов. Если оно равно 1, правильные ответы отмечаются. Значение prnsimple определяет, печатать ли все заголовки, определенные для теста. Если оно равно 1, печатаются только основные заголовки.
\newcount\printotv
\printotv=0 \
newcount\prnsimple
\prnsimple=0
Значения этих переменных для любой части документа задаются командами
- \printsimple Печатать без заголовков.
- \printall Печатать с заголовками.
- \setprinttrueotv Помечать правильные ответы на печати
- \setnoprinttrueotv Не помечать правильные ответы на печати
- \trueotv Маркер правильных ответов.
Указанные команды действительны для оставшейся части текста, пока не встретится отменяющая команда. Приведем определения этих команд:
\def\printsimple{\prnsimple=1}
\def\printall{\prnsimple=0}
\def\setprinttrueotv{\printotv=1}
\def\setnoprinttrueotv{\printotv=0}
\def\trueotv{\ifnum\printotv=1$\bullet $\ \fi}
Кроме того, вид печатного документа определяют переменные шаблона, определяющие последующий текст, которые можно задать в любом месте документа. Дальнейшее рассмотрение ведется на примере экзаменационных билетов. Приведенные ниже команды позволяют определить ряд строк в заголовке – например, в экзаменационных билетах это строки «Утверждаю», «Предмет», «Специальность», «Зав каф», «Составил». Данные команды определяют, в свою очередь, новые команды, используемые внутри шаблона.
\def\templateutv#1{\edef\ltemplateutv{#1}}
\def\templatepredm#1{\edef\ltemplatepredm{#1}}
\def\templatespez#1{\edef\ltemplatespez{#1}}
\def\templatezavkaf#1{\edef\ltemplatezavkaf{#1}}
\def\templatesost#1{\edef\ltemplatesost{#1}}
Аналогично определяется ряд других параметров теста, таких как название подразделения, фамилия руководителя, дата утверждения, название организации, фамилия составителя, заголовок теста, название предмета, шифр специальности.
\def\kafedra#1{\edef\lkafedra{#1}}
\def\zavkaf#1{\edef\lzavkaf{#1}}
\def\datautv#1{\edef\ldatautv{#1}}
\def\vuz#1{\edef\lvuz{#1}}
\def\bilettitle#1{\edef\lbilettitle{#1}}
\def\biletauthor#1{\edef\lbiletauthor{#1}}
\def\predmet#1{\edef\lpredmet{#1}}
\def\spez#1{\edef\lspez{#1}}
Переопределение строк шаблона можно сделать в любом месте документа; оно имеет силу, пока не встретится новое определение.
Выполняется начальная инициализация переменных до определения шаблонов и окружений, где они используются.
\templateutv{УТВЕРЖДАЮ}
\templatepredm{Предмет}
\templatespez{Специальность}
\templatezavkaf{Зав.кафедрой}
\templatesost{Составил}
\kafedra{<Название кафедры>}
\zavkaf{<Фамилия И.О.>}
\datautv{<чч.мv.гг>}
\vuz{<Название ВУЗа>}
\bilettitle{<Заголовок билета>}
\biletauthor{<Фамилия И.О.>}
\predmet{<Название предмета>}
\spez{<Специальности>}
Команды для компоновки отдельных тестов
Для работы с отдельными тестами предусмотрена команда для заголовка теста \BiletHeader. Результат ее выполнения зависит от переменных шаблона. Происходит установка счетчика номеров вопросов VoprosNumber равным 1, а счетчик номеров билета BiletNumber увеличивается на 1.
\newcommand{\BiletHeader}{%
\ifnum\prnsimple=0
\nopagebreak \par
\noindent\textbf{\ltemplateutv}
\vspace{0.4cm}\nopagebreak
\par
\ \ltemplatezavkaf <<\lkafedra >>
\par%
\rule[-1pt]{2 cm}{0.4pt} \lzavkaf
\vspace{0.2cm}\nopagebreak
\par%
\ \ltemplatepredm : <<\lpredmet >>\nopagebreak \par
\ \ltemplatespez : \lspez\nopagebreak \par
\ldatautv
\vspace{0.1cm}\nopagebreak \par
\begin{center}
\textbf{\lvuz}\nopagebreak
\end{center}
\fi
\begin{center}
\textbf{\lbilettitle \No \arabic{BiletNumber}}\nopagebreak
\vspace{0.2cm}\nopagebreak
\addtocounter{BiletNumber}{1}
\setcounter{VoprosNumber}{1}
\nopagebreak
\end{center}
}
\newcommand{\BiletFooter}{%
\ifnum\prnsimple=0
\vspace{0.4cm}\nopagebreak \par
\noindent\ltemplatesost
\rule[-1pt]{1.5cm}{0.4pt}
\lbiletauthor \nopagebreak
\vspace{0.1cm}
\fi
\par
}
Окружение для билета, в котором используются обе вышеприведенные команды для заголовка и нижней части билета –
\newenvironment{EkzBilet}{\BiletHeader}{\BiletFooter }
Окружение для списка вопросов –
\newenvironment{ListQvery}{%
\par\noindent\textbf{\ltemplateutv}
\vspace{0.4cm}
\\
\ \ltemplatezavkaf <<\lkafedra >> \rule[-1pt]{3cm}{0.4pt} \lzavkaf
\vspace{0.2cm}
\\
\begin{center}
\textbf{Список вопросов по курсу <<\lpredmet >>}\\
для специальности \lspez
\end{center}
\setcounter{VoprosNumber}{1}
}{\BiletFooter}
Данное окружение использовано в команде для распечатки списка вопросов.
\def\makelistqvery{%
\begin{ListQvery}
\setcounter{VoprosNumber}{1}
\foreachArray{qvery}{\par\noindent\arabic{VoprosNumber}.%
\addtocounter{VoprosNumber}{1}}
\end{ListQvery}
}
Используется команда, распечатывающая все элементы массива qvery. Перед распечаткой очередного элемента выполняется команда распечатать порядковый номер вопроса и увеличить его на 1.
\par\noindent\arabic{VoprosNumber}. \addtocounter{VoprosNumber}{1}
Результат работы команды представлен на рис. 2. Для компоновки отдельного теста можно воспользоваться как вышеприведенными окружениями, так и командой \printbilet, параметром которой являются номера вопросов, включаемых в тест, разделенные командой \and. Результат работы команды представлен на рис. 3.
\long\def\printbilet #1{
\let\and=\printQ %
\begin{EkzBilet}
\printQ #1
\end{EkzBilet}
}
Команды для компоновки тестов
Для компоновки блоков из 2-х и 4-х тестов используется окружение tabular. Определена команда \twotestblok для компоновки блока из 2-х тестов, которая имеет два параметра, задаваемых по аналогии с параметром команды \printbilet. Результат работы команды показан на рис. 4.
\long\def\twotestblok#1#2{
\begin{tabular}{p{0.9\linewidth}}
\hline \\ \printbilet{#1}\\ \hline \\ \printbilet{#2} \\ \hline %
\end{tabular}
}
Определена команда \fiertestblok для компоновки блока из 4-х тестов, которая имеет четыре параметра, задаваемых по аналогии с параметром команды \printbilet. Результат ее работы – на рис. 5.
\long\def\fiertestblok#1#2#3#4{
\small
\begin{tabular}{p{0.45\linewidth}|p{0.45\linewidth}}
\hline \\ \printbilet{#1} & \printbilet{#2} \\ \hline \\ %
\printbilet{#3} & \printbilet{#4} \\ \hline
\end{tabular}
}
Все рассмотренные выше команды можно свести в отдельный файл testsmakr, подключаемый командой \input{testsmakr}. Пример рабочего файла, при компиляции которого получены результаты, представленные на рисунках, мы предоставляем собрать читателю. (Те, кто хочет убедиться в правильности своего результата, найдут полный текст на стр. 111.)
В данном примере определены десять вопросов, которые используются при выводе тестов при помощи рассмотренных в статье команд.
Заключение
Вместо множества команд для вставки каждого фрагмента пользователю достаточно запомнить лишь пять:
1 Команды– ограничители начала и конца запоминаемого фрагмента
2 Команды вставки отдельного фрагмента
3 Команды вставки всех фрагментов или их части
4 Команды вставки блоков из двух или четырех билетов
5 Два новых окружения, специализированных для подготовки тестов: список вопросов и билет
Команды и окружения, указанные в 4-м и 5-м пунктах, в отличие от пунктов 1 – 3, специализированы для подготовки тестовых заданий. Кроме того, при выводе всех фрагментов имеется возможность выполнения произвольного форматирования над каждым фрагментом.
Преимуществом подобного подхода является возможность для автора сосредоточиться на содержании самих вопросов, а не на их оформлении при компоновке тестов. Значительно упрощается корректировка вопросов. Править необходимо только в одном месте без использования поиска и автозамены, как при использовании копирования – вставки из буфера обмена в MicrosoftWord.
Результатом использования рассмотренных в статье макрокоманд является повышение эффективности работы за счет переложения на компьютер рутинных задач. |