
Недавно я разместил вопрос о написании макроса на простом TeX, который будет изменять элементы в указанном пользователем списке. Я небрежно упомянул о своей нелюбви к интерфейсу TeX, поэтому кто-то перевел меня на LaTeX3, который, как и обещает руководство, должен быть больше похож на современный язык программирования. Я только начинаю программировать на TeX, поэтому мне пришлось нелегко, пытаясь понять руководство. Я предполагаю, что это потому, что руководство было написано для опытных пользователей TeX; однако, похоже, что для людей, только начинающих работать с LaTeX3/TeX, нет альтернативы, поэтому у меня нет выбора, кроме как работать с тем, что у меня есть. Вот почему я публикую это. Руководство запутанное, и я хотел бы прояснить часть этой путаницы, задав вам несколько простых вопросов о синтаксисе.
Я должен упомянуть, что человек, который рассказал мне о LaTeX3, также дал мне решение для моего исходного поста с использованием его интерфейса. Я смог использовать это решение в сочетании с руководством, чтобы начать разбираться в некоторых основных фактах о синтаксисе LaTeX3. Я собираюсь рассказать о том, что мне удалось выяснить, но честно предупреждаю, что часть этого основана на моих собственных выводах, сделанных с помощью примера, предоставленного пользователем stackexchange, а не на явных инструкциях в руководстве, так что ждите ошибок. Я хотел бы, чтобы вы знали, я не запутываю ситуацию, когда иногда использую собственную терминологию. Просто сложно говорить о предмете, который вы не полностью понимаете, структурированным образом.
Также я пишу это как отдельный пост, а не комментарий, из-за его длины. Заранее спасибо.
-----------------------------------------------------------------------------------------------------------------------
Определения функций.
Что мне удалось выяснить на данный момент:
Новая функция определяется, среди прочего, с помощью следующего кода:
\cs_new_<restrictions>:Npn <function name> <function parameters> {<replacement code>}
Это \cs_new_<restrictions>
команда LaTeX, Npn сообщает «анализатору» интерфейса, что ему следует ожидать после части \cs_new_<restrictions>: Npn
кода, в данном случае — одно контрольное слово токена, т. е. <function name>
один или несколько параметров, т. е. <function parameters>
, и список токенов, т. е. {<code>}
, который заменяет функцию.
Итак, если я хочу определить новую функцию, которая принимает, скажем, 4 аргумента, я могу сделать это с помощью следующего кода:
\cs_new_<restrictions>:Npn \myfunction #1 #2 #3 #4 {<code>}
Аналогично код для функции с 2 аргументами может выглядеть примерно так:
\cs_new_<restrictions>:Npn \myfunction #1 #2 {<code>}
Конечно, я предполагаю (и поправьте меня, если я ошибаюсь), что пробелы не нужны, поскольку синтаксическому анализатору уже сказано, как разграничивать «мета»-аргументы ( <function name>
, <parameters>
, {<code>}
) друг от друга с помощью «мета-сигнатуры».Нпн.
Теперь, если я хочу избавиться от #, я могу использовать следующую общую команду
\cs_new_<restrictions>:Nn <function name>:<function signature> {<code>}
Аналогичная ситуация, за исключением того, что теперь анализатор ожидает что <function signature>
-то вроде Nn, NnN, TnN или что-то в этом роде после <function name>
.
Итак, функция с 4 аргументами может выглядеть так:
\cs_new_<restrictions>:Nn \myfunction:NNNN {<code>}
и один с 2 аргументами вроде этого
\cs_new_<restrictions>:Nn \myfunction:NN {<code>}
В файле есть и другие команды.l3basicsбиблиотека для создания функций, но их общая структура, по сути, та же самая. Единственное отличие в их функциональности. Так, например, использование \cs_set...
вместо \cs_new...
делает функцию локальной, а не глобальной. Я, вероятно, напишу следующий пост с просьбой подробнее рассказать о том, что такое расширения e-type и x-type, но сейчас я думаю, что лучше придерживаться общей картины.
Ну, так ли это на данный момент?
Ладно, двигаемся дальше.
Определения переменных.
Что мне удалось выяснить на данный момент:
Итак, в LaTeX3 довольно много типов данных, но основными являютсясписки токенов,струны,целые числа,последовательности, исписки, разделенные запятыми. Каждый из них использует свои собственные сокращения, но в целом при определении новой переменной вы объявляете тип и сопровождаете его ключевым словом, напримерновыйиликонстантав зависимости от того, инициализируете ли вы переменную.
Например, если я хочу объявить, но не инициализировать,список токеновпеременная Я использую код:
\tl_new:N \mytokenList
и затем где-то в будущем я могу сохранить список токенов \mytokenList
с помощью кода:
\tl_set:Nn \mytokenList {<tokens>}
Но если я знаю, какие данные я хочу сохранить в переменной с самого начала, я могу вместо этого использовать эту команду (не применимо кпоследовательностиилицелые числа)
\tl_const:Nn \mytokenList {<tokens>}
Отступление: я заметил, что даже переменные имеют "сигнатуры функций". Вероятно, это упрощает определение режима синтаксического анализа.
Это, пожалуй, самое общее, на что я способен, прежде чем мне придется уточнять, о каком типе данных я говорю, поскольку с каждым типом связаны свои собственные операции.
-----------------------------------------------------------------------------------------------------------------------
Вот что у меня есть на данный момент. Буду признателен за любые отзывы. Этому нелегко научиться самостоятельно! Особенно с минимальными знаниями TeX, поэтому прошу прощения, если некоторые из вас смотрят на это и думают: "ну, очевидно". В любом случае, спасибо еще раз.
решение1
Существует два основных способа определения функций:
\cs_new<restrictions>:Npn
\cs_new<restrictions>:Nn
где может быть _protected
, _nopar
или _protected_nopar
.
Оба способа проверяют, что N
следующий аргумент (то есть один токен) является управляющей последовательностью (или активным символом), которая в данный момент не определена иглобальноопределить последовательность управления.
В чем разница? В том, что первое семейство требует после определения управляющей последовательности «текста параметра» перед {
разграничением «текста замены» функции.
«Текст параметра» может быть любой последовательностью токенов, включая #1
, #2
и так далее до #9
. Однако, чтобы оценить всю мощь этой свободы, вам необходимо ознакомиться с главой 20 TeXbook и понятием «разграниченного аргумента».
Но давайте не будем усложнять. Два следующих фрагмента кода полностью эквивалентны:
\cs_new:Npn \harry_foo:nn #1 #2 { -#1-#2- }
\cs_new:Nn \harry_foo:nn { -#1-#2- }
поскольку последний автоматически предоставит текст параметра #1#2
на основе сигнатуры определяемой функции, в данном случае :nn
.
Подпись должна состоять из (возможно пустой) последовательности символов n
и N
.
Обратите внимание, что пробелы игнорируются, когда \ExplSyntaxOn
активен, поэтому
\cs_new:Npn \harry_foo:nn #1 #2 { -#1-#2- }
\cs_new:Npn \harry_foo:nn #1#2 { -#1-#2- }
\cs_new:Npn \harry_foo:nn #1#2{ -#1-#2- }
все эквивалентны. Пробел может быть даже после #
, но я бы не рекомендовал этого.
Правила синтаксиса TeX указывают, что когда ожидается «текст параметра» (в основном, при выполнении \def
или подобных назначений и после сохранения имени макроса, который должен быть определен),вседо первого {
является частью текста параметра. Невозможно предсказать, что такое текст параметра, поэтому используется специальный p
спецификатор аргумента, который просто означает «все до {
».
Только простой текст параметров, такой как #1
, #1#2
и т. д., может быть сгенерирован автоматически, что и происходит при использовании второго семейства \cs_new<restrictions>:Nn
.
Где вы не правы? В предположении, что можно использовать T
как спецификатор в сигнатуре. Аргумент спецификаторы T
или F
добавляются, когда \prg_new_conditional<restrictions>:Nnn
выполняется.
Кроме того, ваш анализ текста параметра неверен, как было показано ранее.
Что насчет \cs_set<restrictions>:Npn
and :Nn
? Все, что указано выше, применимо с той разницей, что определяемая функция не проверяется на предмет определения или отсутствия, и ее значение будет молча перезаписано, но область действия объявления совпадает с текущей группой. Обычно \cs_set...
используется для временных функций, которые необходимо адаптировать к контексту, поэтому их значение не является фиксированным.
Соглашения об именовании переменныхрекомендуетчто их имя начинается с l
, g
или c
. На самом деле переменные, используемые в expl3
коде, должны соответствовать соглашению; можно использовать «нормальные» имена, например, \myTokenList
для переменных типа tl
(возможно, также clist
), которые должны использоваться в документе.
Переменные, начинающиеся с , l
всегда должны обрабатываться локально ( \tl_set:Nn
например, ), в то время как переменные, начинающиеся с , g
всегда должны обрабатываться глобально ( \tl_gset:Nn
например, ).
Переменные, начинающиеся c
сконстантыи должно бытьникогдане подвергались действию после присвоения им значения, а только использовались.
Константы можно определить с помощью
\tl_const:Nn \c_harry_foo_tl {<tokens>}
\str_const:Nn \c_harry_foo_str {<tokens>}
\clist_const:Nn \c_harry_foo_clist {<comma list>}
\seq_const_from_clist:Nn \c_harry_foo_seq {<comma list>}
\prop_const_from_keyval:Nn \c_harry_foo_prop {<key-value list>}
\int_const:Nn \c_harry_foo_int {<integer expression>}
\fp_const:Nn \c_harry_foo_int {<fp expression>}
\bool_const:Nn \c_harry_foo_bool {<boolean expression>}
\dim_const:Nn \c_harry_foo_dim {<dimen expression>}
\skip_const:Nn \c_harry_foo_dim {<skip expression>}
\muskip_const:Nn \c_harry_foo_dim {<muskip expression>}
\intarray_const_from_clist:Nn \c_harry_foo_intarray {<comma list>}
\regex_const:Nn \c_harry_foo_regex {<regex>}
\cc_tab_const:Nn \c_harry_foo_cctab {<code>}