Latex3: Объявление локальных переменных

Созданный на 18 окт. 2017  ·  49Комментарии  ·  Источник: latex3/latex3

В документе «Пакет экспло3 и программирование на LaTeX3» (v. 2017/09/18) говорится на стр. 7:

Соглашение о кодировании LaTeX3 заключается в том, что все переменные должны быть объявлены перед использованием.

Точно так же в документе Интерфейсы LaTeX3 (версия 2017/09/18) говорится на стр. 10:

... в отличие от переменных, которые всегда нужно объявлять

expl3 предоставляет средств для локального объявления переменной; только глобально. Однако по-прежнему можно создать локальную переменную неявно, используя различные функции \..._set:N... .

Принцип структурированного программирования заключается в том, что следует избегать ненужного сброса переменных в глобальную область видимости. Язык TeX, как и все известные мне языки программирования высокого уровня (включая C, C #, Fortran, Java, Lisp, Pascal и Python), предоставляет конструкцию области видимости для создания локальных переменных, а именно групп. Более того, сам язык программирования LaTeX3, к счастью, поддерживает создание локальных переменных, несмотря на то, что предлагают руководства.

На мой взгляд, приведенные выше предостережения следует удалить из руководств и пояснить, что переменные могут и должны создаваться локально, когда это возможно.

Более того, если действительно желательно поощрять стиль программирования, в котором все переменные объявляются до их использования (я лично считаю, что это вопрос стиля программирования, который следует оставить на усмотрение программиста), функции должны быть предусмотрены для объявления локальных переменных так же, как есть функции для объявления глобальных переменных.

expl3 feature-request

Все 49 Комментарий

Текущая позиция соответствует ряду экспериментов, проведенных командой по созданию шаблона, который работает с основами TeX, поддерживающими expl3 . В частности, важно отметить, что переменные могут быть реализованы с использованием макросов или регистров, и иметь в виду, как работает группировка TeX.

При использовании регистров (например, для типа int ) нам нужен распределитель, чтобы связать номер регистра и cs, которые мы используем для доступа. Напротив, для хранилища на основе макросов это не нужно. Таким образом, хотя \cs_set_eq:NN можно использовать для генерации новых имен tl (например), он не сработает ни с чем, использующим регистры:

\int_set_eq:NN \l_undeclared_int \l_tmpa_in

вызовет ошибку.

Можно создать локальный распределитель регистров, и мы уже пробовали это в прошлом для _e.g._ \int_local_new:N . (См. Пакет etex для одной из таких реализаций или просмотрите историю expl3 .) В конце концов, команда отказалась от этого, поскольку это, похоже, не очень хорошо «вписывается» в область видимости TeX. Ключевым моментом здесь является то, что группировка TeX основана не на объявлениях, а на явных группах, и что локальная переменная существует внутри любых вложенных групп:

\begingroup
  \def\foo{}
  \begingroup
  \show\foo

Когда мы попробовали локальное _создание_ переменных, казалось, что существует опасность скрыть, как работает группировка TeX, и что программисты могут быть введены в заблуждение.

Поэтому мы решили использовать _объявление_ переменных «все глобальное», но с локальной и глобальной _установкой_ таких переменных (соглашение \l_... _versus \g_... ). Это локализует важную вещь: ценность. Поэтому мы рекомендуем, чтобы все объявления были на верхнем уровне.

\tl_new:N \l_my_tl
\int_new:N \l_my_int

...
\cs_new_protected:Npn \my_func:nn #1#2
  {
    \group_begin:
      \tl_set:Nx \l_my_tl { \tl_lower_case:n {#1} }

На сегодняшний день этот шаблон, похоже, хорошо работает для задач, для которых использовался expl3 .

В tex даже локально определенные переменные не уничтожаются полностью после окончания группы. Они по-прежнему используют некоторое строковое пространство: https://tex.stackexchange.com/questions/316999/release-space-in-the-string-pool. Так что, имхо, лучше рассматривать переменные как глобальные объекты.

@ u-fischer Хороший замечание: не тот, который мы действительно рассматривали в то время, но о котором стоит помнить.

На мой взгляд, такой подход ошибочен. Это противоречит принципам структурного программирования и TeX, который позволяет создавать локальные переменные. Это также делает LaTeX3 несовместимым, поскольку некоторые типы данных поддерживают локальные переменные, а некоторые нет.

Я прошу вас восстановить локальное создание переменных всех типов. Это дает программистам выбор: те, кто предпочитает определять все переменные глобально, могут это делать, и те, кто желает определять некоторые глобально, а некоторые локально, тоже могут это делать.

Если есть предостережения, связанные с памятью или иначе, их можно упомянуть в документации.

18 октября 2017 года в 21:17 EvanAad [email protected] написал:

На мой взгляд, это ошибочный подход, и я хотел бы попросить, чтобы
вы заново устанавливаете локальное создание переменных. Это дает программистам
выбор: те, кто предпочитает определять все переменные глобально, могут это сделать, и
те, кто желает определить некоторые глобально, а некоторые локально, тоже могут это сделать.

для переменных, основанных на типе регистра, вы распределяете ресурсы из
фиксированный глобальный пул, поэтому глобальное распределение имени на самом деле намного больше
естественный. Учитывая, что мы предполагаем, что etex имеет более 256 регистров
каждый тип, поведение распределения может быть скрыто больше, чем это
мог бы с классическим тексом, но это все еще фундаментальная особенность
лежащая в основе система TeX. explo3 никогда не может быть полностью общим программированием
язык: он должен работать с базовой системой TeX. Ты не должен быть
по сравнению с C #, но с другими языками на основе TeX и, в частности, с
\ newcount и друзья.

Также неверно утверждать, что все другие языки позволяют переменным быть
объявлены в локальных областях видимости, например, fortran позволяет только переменным быть
объявляется в начале функции / подпрограммы.

Ничто из вышеперечисленного не означает, что мы определенно не будем добавлять локальное объявление.
системы, но обращение к языкам общего назначения - не лучший вариант использования.
При наборе текстов должны быть разумные варианты использования, где
использование глобальной системы деклараций было проблемой.

Тот факт, что Fortran требует, чтобы переменные объявлялись в начале функции / подпрограммы, не означает, что эти переменные являются глобальными. Если вы настаиваете на поощрении стиля программирования, в котором должны быть объявлены все переменные, пусть будет так, но затем предоставьте функции для объявления локальных переменных, соответствующих тем, для объявления глобальных.

Обоснование определения объема и локальных переменных является в первую очередь концептуальным. Тот факт, что базовый движок TeX не освобождает некоторые ресурсы, связанные с локальными переменными, нельзя сбрасывать со счетов, но, на мой взгляд, это не может быть причиной отказа от понятия области видимости и локальных переменных. За исключением опытных программистов, основная реализация не имеет значения; а для опытных программистов в документации можно предложить советы и предостережения.

обращение к языкам общего назначения - не лучший вариант использования.

Я бы сказал, что отклонение от принципов структурированного программирования, которые были реализованы практически на всех языках программирования с 1960-х годов до сегодняшнего дня, включая TeX, должно быть чем-то, что у команды LaTeX3 должно быть для этого должны быть очень веские причины. Бремя доказательства должно лежать на тех, кто желает отменить то, что повсеместно считается хорошей практикой программирования и что уже существует в TeX; не на тех, кто хочет его сохранить.

@EvanAad Список токенов, последовательность списка запятых и переменные списка свойств на самом деле являются макросами TeX, а переменные другого типа - нет. Предположим, что локальное объявление целочисленной переменной разрешено. Это потребляет регистр, и необходимо глобально позаботиться об этом, чтобы распределение не затрагивало уже назначенный регистр.

Скажем, вашей локальной целочисленной переменной \x назначен регистр 100, который уже занят переменной \y на верхнем уровне; использование \y на том же уровне, где \x определено локально, было бы просто катастрофой. Таким образом, необходим глобальный учет назначенных регистров, что очень затрудняет освобождение локально назначенных регистров. Резервирование блока регистров для локальных назначений не является решением.

Я не говорю, что это невозможно, но я не думаю, что это стоит боли.

вам не хватает того факта, что \ newcount не просто локально (или глобально)
объявляя имя, оно связывает имя с определенным извне фиксированным
ресурс. есть только один счетный регистр 42 и все имена, которые
объявлены как означающие, что count42 относятся к одному и тому же регистру, независимо от того,
присвоение имени может быть локальным или глобальным. Как я уже упоминал, сравнения с другими
языки не так полезны, но если вы хотите сравнить, вы должны
сравнить распределение файловых потоков или что-то подобное: вы не всегда можете изолировать
локальные объявления, когда они взаимодействуют с определенным извне
ресурс.

18 октября 2017 года в 21:43 EvanAad [email protected] написал:

Тот факт, что Fortran требует, чтобы переменные объявлялись в начале
функция / подпрограмма не означает, что эти переменные являются глобальными. Как и я
написал, если вы настаиваете на поощрении стиля программирования, в котором все переменные
должны быть объявлены, пусть будет так, но затем предоставьте функции для объявления локальных
переменные, соответствующие тем, для объявления глобальных.

Обоснование масштабирования и локальных переменных прежде всего
концептуальный. Тот факт, что базовый движок TeX не выпускает некоторые
ресурсов, связанных с локальными переменными, нельзя исключать,
но, на мой взгляд, это не может быть причиной отказа от понятия
области видимости и локальных переменных. За исключением мощных программистов,
базовая реализация не имеет значения; а для опытных программистов советы
и предостережения могут быть предложены в документации.

-
Вы получили это, потому что прокомментировали.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/latex3/latex3/issues/410#issuecomment-337721923 или отключить звук
нить
https://github.com/notifications/unsubscribe-auth/ABNcAimMfBDqA-e96Q7tkS-ERr5fv_2Mks5stmLqgaJpZM4P-Mpq
.

Ключевым моментом является то, что, как упомянули Джозеф и Дэвид, возможность локального определения int s может бытьуже была ) выполнена. Я думаю , что это стоит боли, чтобы создать язык , который представляет последовательный уровень абстракции , который находится в согласии с принципами структурного программирования.

Однако мне легко сказать, что это стоит боли, потому что я не тот, кто испытывает боль при реализации языка LaTeX3. Итак, давайте проявим милосердие и скажем, что на самом деле это не стоит боли. Отлично. В этом случае разделите типы данных на две категории: те, которые позволяют создавать локальные переменные, и те, которые не позволяют. И опишите эти категории в руководствах. Не говорите: все переменные должны быть объявлены перед использованием. Скажите: «Есть две категории типов данных. Первая, состоящая из cs , tl , clist , ... вмещает локальные переменные, а вторая, состоящая из int , ... нет ". И объясните, почему существует вторая категория. Таким образом, документация соответствует действительности, а программисты хорошо осведомлены и имеют возможность выбора. Некоторым программам вообще не нужно использовать типы данных второй категории, и в этих случаях зачем программисту объявлять глобальные переменные?

@EvanAad Я подозреваю, что это связано с тем, что я привык к программированию TeX (включая «традиционные» ограничения TeX90), но в настоящее время я не уверен, в чем проблема с текущим подходом. Мы поддерживаем и действительно поощряем переменные, назначаемые локально: они очень распространены и действительно являются частью синтаксиса, который у нас есть для именования переменных ( \l_... / \g_... ). Тот факт, что они выделены / зарезервированы глобально, этому не мешает.

Кстати, мы не хотим связывать интерфейсы с реализацией: например, тип данных prop имел по крайней мере пару известных мне реализаций, одна из которых использовала регистры, а текущая - макросы.

Чтобы добавить к этому еще одно примечание, соломинка, которая сломала спину верблюда (в моей памяти), из-за того, что локальная система распределения регистров / переменных упала, была из-за несогласованности . Поскольку макросы и регистры вели себя по-разному, вы получили другое поведение при написании

\group_begin:
  \int_new_local:N \l_tmpa_int
  \int_gset:Nn \l_tmpa_int {7}
\group_end:
% `\l_tmpa_int` undefined

против

\group_begin:
  \tl_new_local:N \l_tmpa_tl
  \tl_gset:Nn \l_tmpa_tl {7}
\group_end:
% `\l_tmpa_tl` defined as `7`

И единственный способ решить эту проблему - написать систему распределения для макросов, которая никогда серьезно не рассматривалась из-за накладных расходов. (TeX замедляется, чем больше определено переменных, поэтому удвоение количества списков токенов могло бы заметно повлиять на производительность.)

Я все еще считаю, что expl3 поступил правильно, абстрагировав макросы и регистры в функции, которые выглядят и ощущаются одинаково - и если обратная сторона заключается в том, что синтаксис, естественно, не поддерживает действительно локальные переменные и регистры, которые видели чрезвычайно ограниченное использование в TeX по причинам, изложенным выше, то, с моей точки зрения, это приемлемый компромисс.

@wspr Но примеры, которые вы привели как те, которые сломали верблюжью спину, являются примерами, когда программист злоупотреблял языком, глобально назначая локальную переменную. Это на программаторе. Текущая система не решает проблему злоупотребления языком, поскольку программист все еще может использовать переменную \l_... как если бы она была глобальной.

... что согласуется с принципами структурного программирования.

это довольно смелое заявление, учитывая, что существует более одного набора
принципов и с учетом того, что попытки объединить разные принципы в
суперсеты обычно создавали большие, но бесполезные языки в прошлом.

Решающим моментом не обязательно является то, что что-то можно сделать (поскольку
внутри полных баз Тьюринга все равно на этом уровне), но
можно ли что-то делать эффективно и последовательно и т. д.

@wspr : Почему ты как хулиган закрыл этот выпуск? Это что угодно, только не решенное.

@EvanAad - просто пытаюсь навести порядок. Обсуждение, безусловно, может продолжаться, и, если это будет оправдано, мы откроем его снова.

Я временно блокирую эти проблемы из-за горячих комментариев.

Думаю, это актуальный вопрос. Одним из результатов этого обсуждения должно быть, по крайней мере, более подробное описание в документе того, почему мы выбрали только глобальные декларации. @EvanAad Я не думаю, что вы вполне понимаете ограничения системы TeX, но позвольте нам отступить и на мгновение проигнорировать эту проблему. Не могли бы вы привести пример (скажем, из 10-20 строк) с использованием гипотетической \int_local_new:N или другой системы по вашему выбору с семантикой по вашему выбору? Это поможет сформулировать обсуждение в более конкретной обстановке, и мы сможем выделить преимущества / недостатки.

Предположительно нам нужно дождаться снятия замка, я не знаю, как это сделать. В любом случае большинство участников этого разговора будут спать несколько часов. (Кстати, я не думаю, что копирование # 410 в # 411 в качестве беспорядочного обсуждения было очень разумным.)

Учитывая тот факт, что это довольно подробное обсуждение, я думаю, что стоит резюмировать техническую и социальную историю, которая привела нас к тому, где мы находимся.

На техническом уровне TeX предоставляет регистры и макросы для хранения. Макросы можно просто «создать» с помощью \def , но для использования регистров по имени нам понадобится некоторый распределитель, чтобы связать номер глобального регистра, _e.g._ \count40 , с некоторым именем, например \mycount . Это было обеспечено с первого дня, когда модель была предоставлена ​​простым TeX \newcount , _etc._. Проще говоря, \newcount распределяется глобально и, несмотря на название, не выполняет никаких проверок. В других форматах, в первую очередь LaTeX2e и ConTeXt, в общих чертах принят этот подход. Они также обычно приняли идею, что отдельные регистры затем _ назначаются_ либо локально, либо глобально, поскольку это предотвращает накопление стека сохранения.

В TeX90 было всего 256 регистров общих типов, поэтому повторное использование регистров в локальном контексте было жизненно важным. Видно, что в _e.g._ graphics где для сохранения «нормального» кода, большому количеству регистров даются новые имена внутри группы и используются исключительно локально. В e-TeX у нас есть на много регистров больше, поэтому такой подход менее необходим: мы можем свободно выделять больше регистров (и макросов) для специальных целей. В частности, это означает, что у нас нет ничего похожего на тех, кто вынужден «перерабатывать» регистры под несколькими именами.

Кодовая база expl3 находилась в разработке _долго_ и изначально не требовала e-TeX. Разработка expl3 также выполняется в основном «поверх» LaTeX2e с общим принципом: expl3 не загрязняет пространство имен документа и не меняет базовое поведение LaTeX2e.

В частности, мы должны иметь в виду, что \newcount LaTeX2e похож на простой: он распределяет глобально и не проверяет. Таким образом, в частности

\def\foo{%
  \begingroup
    \newcount\localfoocnt

это «плохой стиль», так как если \foo вызывается несколько раз, мы будем использовать регистры, которые никогда не освобождаются.

Для expl3 команда попыталась избежать привязки задокументированного поведения переменных к реализации макросов или регистров. (Я действительно отмечаю, что мы разрешаем использовать tl без аксессуара, который в конечном итоге полагается на то, что они являются макросами.) Мы также по-прежнему используем регистры для целых чисел, размеров и т. Д., Поскольку они доступны и предлагают лучшая производительность и «самозавершение», чем выполнение всего в макросах. (Это можно было бы сделать с помощью e-TeX и различных примитивов \<thing>expr .) Таким образом, нам нужна система распределения, и для согласованности мы выделяем все типы переменных, а не только те, которые основаны на регистрах. . Как заметил @EvanAad , когда проверка неактивна, можно сделать _e.g._ \tl_set_eq:NN \l_new_tl \l_existing_tl без ошибок, но это не поддерживаемое поведение (например, проверка отметит это).

В рамках распределителя expl3 было принято решение, что new _would_ проверяет наличие, в отличие от \newcount , поэтому

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new:N \l_my_tl

вызовет _error_, если \my_foo: используется повторно. Это подталкивает в направлении, которое было стандартной практикой TeX с первых дней работы с plain: выделение памяти выходит за рамки макроса. (Обратите внимание, что на простом языке \newcount - это \outer поэтому внутри макросов "запрещено".)

\tl_new:N \l_my_tl
\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_clear:N \l_my_tl

Пакет etex много лет назад представил распределитель регистров, который может выполнять как локальное, так и глобальное распределение регистров, \loccount _versus_ \globcount , _etc. Некоторое время expl3 загружал etex и использовался для обеспечения функциональности \int_local_new:N и т.п. Учитывая тот факт, что большинство программистов TeX привыкло к глобальному распределению памяти, неудивительно, что это не получило столь широкого распространения. Однако команду также беспокоили возможные недоразумения. Что-то вроде

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_local_new:N \l_my_tl

у нас есть вопрос, что происходит при вложенных вызовах \my_foo: или, что более вероятно, в местах, где должно использоваться какое-нибудь «простое» имя, такое как \l_my_tmp_tl . Исходя из общей идеи, что мы хотим, чтобы new выполнял проверки, это должно быть ошибкой. Программистам, работающим на многих других языках, это может показаться странным. Конечно, это соответствует правилам области видимости TeX.

Примечательно, что изменения в ядре LaTe2e в последние годы означают, что мы больше не хотим загружать etex (действительно, мы много работали, чтобы сделать expl3 'самодостаточным'). Таким образом, любой новый локальный распределитель должен быть написан для работы с LaTeX2e без «мешающего» поведения для тех, кто не использует expl3 : выполнимо, но не совсем тривиально.

Для чего-то вроде \l_my_tmp_tl можно, конечно, по-прежнему выполнять глобальное распределение, но тогда нужно знать, какие переменные \l_... локально выделены в текущей группе, а какие глобально выделены, но назначены локально.

У меня нет тестовых данных, но, вероятно, стоит отметить, что выделение переменной - это больше работы, чем просто ее установка (конечно, когда проверка не активна), поэтому использование локального распределителя будет немного медленнее, чем глобальный распределитель плюс местные задания.

Таким образом, по сочетанию технических причин и «соответствия истории» мы решили придерживаться строго глобального _allocation_. Это никоим образом не препятствует локальному присваиванию переменных, что поощряется и очень широко используется.

Это подводит нас к «социальному» элементу. Принятию expl3 в последние годы сильно помогло то, что детали стали "в целом" стабильными. На каком-то этапе команда должна принять решение по поводу материала, частично для того, чтобы люди могли его использовать, а частично для того, чтобы мы перешли к другим задачам. Это не мешает нам пересматривать вещи, но более устоявшиеся условности нуждаются в уважительной причине для изменения.

Здесь, в настоящее время, я думаю, я не вижу, что не так с «обычным» подходом

\tl_new:N \l_my_tl
\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_clear:N \l_my_tl

по крайней мере, до того момента, когда нам потребуется вернуться и изменить текущую настройку.

@blefloch Да, я согласен с тем, что моя реакция на закрытие темы @wspr была необоснованной, и я приношу свои извинения ему и всем остальным. Я чувствовал, что меня увольняют и заставляют замолчать, и я отреагировал на это в истерике. Это неприемлемо. Прости за это.

@josephwright спасибо за подробный ответ. Поразмыслив над этим вопросом, я понял, что если я использую только \<module>_clear_new:N , то, по сути, все мои переменные будут вести себя так, как если бы они были локальными для окружающей группы, пока я назначаю с помощью \<module>_set... а не \<module>_gset... . Более того, я думаю, что эта практика согласуется с соглашением LaTeX3 об объявлении переменной перед использованием, поэтому я не буду отмечен, если проверки включены.

Следуя этой практике, я бы переписал последний пример @josephwright следующим образом:

\cs_new_protected:Npn \my_foo:
{
    \group_begin:
        \tl_clear_new:N \l_my_tl

Единственный тип данных, который не подходит для этой схемы, - это cs , но, насколько мне известно, создание переменных этого типа с помощью \cs_set:Npn et al. Является законным. без предварительного объявления их, потому что LaTeX3 на самом деле не рассматривает cs как нормальный тип данных наравне с другими (это отдельная проблема, которую я хотел бы обсудить с вами, ребята, но я оставлю это в другой поток). Используя этот подход, переменные cs также могут быть созданы локально. Если я хочу сохранить единообразный синтаксис, я всегда могу написать свою собственную оболочку \cs_clear_new:N .

Итак, насколько я понимаю, теперь вы можете закрыть эту проблему, если хотите, @wspr .

@EvanAad В \<thing>_clear_new:N обратите внимание, что объявление _is_ global, где переменная еще не существует. Так

\cs_new_protected:Npn \my_foo:
{
    \group_begin:
        \tl_clear_new:N \l_my_tl
    \group_end:
}
\my_foo:
\tl_show:N \l_my_tl

покажет, что \l_my_tl определено и пусто, при условии, что вы ранее не установили для него что-то другое.

Я хотел бы присоединиться к @blefloch в надежде, что это обсуждение приведет к улучшению документации. В частности, я думаю, что когда вы пишете, что программист «должен» следовать определенному шаблону кодирования, как в предложениях, которые я цитировал в моем исходном сообщении, документация должна разъяснять, что это «должно» означать. Что будет, если шаблон не будет соблюдаться?

  1. Будет ли поведение языка неопределенным?
  2. Поддерживается ли несоблюдение в настоящее время, но может не поддерживаться в будущих версиях?
  3. Сообщит ли двигатель об ошибке?
  4. Сообщит ли двигатель об ошибке, но только при включенных проверках?
  5. Могут ли возникнуть незначительные неудобства?
  6. Будет ли команда LaTeX3 недовольна ворчать, но никаких ошибок или потери функциональности не произойдет?

В качестве примера для 5 возьмем соглашение о добавлении спецификаций аргументов в конец имени функции. Насколько я могу судить, единственная функция, которая не будет работать должным образом, если это соглашение не соблюдается, - это \cs_new:Nn , но все остальное, включая `cs_ new: Npn ', будет работать отлично.

В качестве примера для 6 возьмем соглашение о маркировке локальных и глобальных переменных с помощью \g_... и \l_... . Насколько я знаю, несоблюдение этой конвенции не повлечет за собой никаких последствий. Все будет работать исправно.

@EvanAad - извинения приняты, и я прошу прощения за то, что закрыл вопрос до завершения обсуждения.

@josephwright - как одно крошечное преимущество преимуществ для местного размещения, если я напишу

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new_local:N \l_my_tl
      ...

тогда я знаю, что \l_my_tl не только свободен от вмешательства извне, но, в отличие от использования функции _clear я знаю, что все следы переменной исчезают за пределами использования функции. Другими словами, просто взглянув на код, я понимаю, что его нельзя использовать в качестве полуглобальной переменной в дальнейшем. (И я могу проверить, что ничего странного не происходит, используя \tl_if_exist_p:N .)

(Необходимо запустить, но можно продолжить позже, если будет какой-то смысл в дальнейшем обсуждении.)

@wspr Да, но это опять же тот факт, что он основан на области действия группы TeX. Таким образом, его можно использовать «полу-глобально» в конструкции вида

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new_local:N \l_my_tl
      \tl_set:Nn \l_my_tl { foo }
      \__my_foo:
     ..
  }
\cs_new_protected:Npn \__my_foo:
  {
    \group_begin:
        \tl_use:N \l_my_foo % Definition from \my_foo: => "foo"
...
  }

что было, по крайней мере, частью нашего мышления.

Я думаю, прежде чем мы закончим, я хотел бы подчеркнуть, что на техническом уровне есть несколько способов создать локальный распределитель регистров.

@josephwright - я никогда не видел в этом запутанного примера; в Matlab, например, они различают подфункции, которые не имеют общей области видимости, и вложенные подфункции, которые это делают. Так что, на мой взгляд, так было всегда, ну почему бы \__my_foo: унаследовать область видимости «внешней» функции? Это полностью соответствует группирующему поведению TeX.

(Извините за продолжение обсуждения. День был долгим. Кто-нибудь действительно заинтересован в продолжении?)

Am 19.10.2017 um 09:23 шриб Джозеф Райт:

Я думаю, прежде чем мы закончим, я хотел бы подчеркнуть, что на
технический уровень есть несколько способов создать локальный реестр
распределитель.

да, но довольно близко к аргументу Тьюринга, т.е. к любой такой реализации
будет очень неэффективным во время выполнения, потому что базовый движок
глобально управляет хранилищем регистров. И экспл3 (на этом уровне)
должен оставаться «достаточно эффективным».

это похоже на, скажем, глобальные и локальные переменные и их мутатор. Скорее
чем тестирование в каждой функции, если она выполняет глобальную операцию с
локальная переменная, концепция по умолчанию присутствует только в именах,
например, \ l _... должен быть локальной переменной и не должен использоваться
с глобальной функцией вроде ..._ gset: Nn, но мы не тестируем это на
время выполнения

Однако мы предлагаем модуль проверки (который работает во сколько раз медленнее).
это гарантирует, что все эти соглашения действительно соблюдаются.

Итак, чтобы вернуться к глобальным / локальным переменным

стандартная концепция Exp3

  • объявлять имя переменной один раз (глобально) - многие, потому что в
    по крайней мере, некоторые из типов имеют только глобальные складские места

  • используйте соглашение об именах \ l_ \ g_ для обозначения локальных и глобальных переменных

который дает вам как глобальные, так и локальные переменные, но имеет ограничения, которые

  • вы не объявляете локальную переменную в начале ее области видимости,
    вместо этого вы либо используете _set, либо _clear_new в этот момент, а последний
    может означать, что имя переменной может появиться в этой точке глобально
    в существовании

  • вне области видимости переменная все еще существует со значением по умолчанию
    значение типа (например, _int равно 0 и т. д.), поэтому вы не получите компилятор
    ошибка, если вы ссылаетесь на такую ​​переменную "по ошибке" за пределами предполагаемого
    сфера

Таким образом, единственное, чего вы не получите, - это возможность объявить
локальная переменная, так что ее имя исчезает за пределами области видимости (и производит
неопределенная ошибка некоторого вида во время выполнения, если упоминается за пределами
заявленный объем).

Чтобы сделать это (что да, возможно), Explo3 должен поддерживать свой собственный
пул ресурсов, выходящий далеко за рамки простой ассоциации между именем и глобальным
бассейн, предлагаемый движком, и это будет означать, что весь доступ будет
очень заметно замедлено выполнение - что идет вразрез с критериями разработки Exp3.

@FrankMittelbach - теперь мне любопытно; действительно ли подход etex.sty для регистров настолько неэффективен? Или, может быть, вы имеете в виду переменные tl, и в этом случае я согласен!

@FrankMittelbach На мой взгляд, важно различать и разъяснять в документации, что является наилучшей практикой, по мнению команды LaTeX3, по сравнению с тем, что является формальным требованием, грамматическим или семантическим, правил языка LaTeX3.

Два правила, которые вы процитировали, а именно:

  • объявить имя переменной один раз (глобально)
  • используйте соглашение об именах \l_ \g_ для обозначения локальных и глобальных переменных

подпадают под эту рубрику соглашений о кодировании, которые команда LaTeX3 считает целесообразными, но ни одно из них не предусмотрено грамматическими правилами языка, и несоблюдение этих соглашений не вызывает ошибок или потери функциональности.

Другими словами, соблюдение этих правил - дело личного вкуса и стиля программирования, и, на мой взгляд, это должно быть четко указано в документации.

это все равно что сказать, что знак 50mh - это не правило, а вождение
условности, и это дело вкуса, если водитель ее придерживается (просто
потому что в большинстве случаев это не сразу проверяется)

если вы используете переменную \ l_ с _gset, вы все равно программируете в TeX, но
вы перестали подчиняться грамматическим правилам Exp3
язык. Это сразу нарушает ваш код? наверное нет, но ты
генерировать наращивание сохраненного пакета (см. индекс TeXbook)

Вы говорите, что это только грамматическое правило, если мы проверяем его на
время выполнения, скажем, каждый вторник?

@FrankMittelbach Конечно, если вы определяете это как часть правил языка, чем это так по определению, но я хочу сказать, что это не должно определяться как часть правил языка, потому что это никогда не проверяется, и потому что несоблюдение этого соглашения само по себе не приводит к ошибке или потере функциональности.

В этом разница между высказыванием «Когда вы пишете по-английски, лучше держать перо в правой руке, потому что это не размазывает чернила». и принятие закона, гласящего, что по-английски нужно писать пером в правой руке. Конечно, вы можете принять такой закон, и есть веские основания для его оправдания, но в конечном итоге выбор, в какой руке держать перо, должен быть оставлен на усмотрение отдельного писателя. И найдутся люди, которые решат не придерживаться этого правила и все равно будут писать так же аккуратно, как и те, кто его придерживается.

Я думаю, что @FrankMittelbach завышает накладные расходы на локальные объявления ( \loccount управляет счетчиками локально, ничего не делает глобальным, так что накладные расходы остаются в порядке).

Я не вижу серьезных проблем с предоставлением \int_local:N и \tl_local:N (= \tl_set_eq:NN #1 \c_empty_tl ) и т. Д., Которые были бы очень похожи на \int_zero_new:N и \tl_clear_new:N но делал бы только "новое" локально. Это требует некоторой работы с регистрами, но не чрезмерно.

@EvanAad, вы должны знать, что

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\tl_put_left:cn { l_my_tl } { foobar \par }
\l_my_tl
\end{document}

@blefloch Я говорю, что вполне нормально объявить переменную внутри макроса (с \<module>_clear_new ), и вам не нужно называть ее \l_amount_paid_int , вы можете просто вызвать это \amount_paid или \amountPaid , как и в любом другом языке программирования. \l_..._int - хорошая мнемоника, чтобы напомнить вам, что это целое число и что предполагается, что оно должно использоваться локально, но вас не следует заставлять использовать это или любую другую мнемонику.

потому что это никогда не проверяется, и потому что не придерживаются этого соглашения
сам по себе не вызывает ошибки или потери функциональности.

но в этом суть

а) он причиняет вред в зависимости от обстоятельств, а именно, когда вы смешиваете
глобальное и локальное присвоение одной и той же переменной

б) мы проверяем по запросу (и прямо сейчас этот код может не быть
функционально, но это было и, вероятно, будет снова в конце концов из-за а))

PS да, я понял вашу точку зрения о том, как держать ручку (левша), и да, я
были выше предельной скорости (на моем велосипеде), но все же я считаю это
правила дорожного движения, а не правила дорожного движения, и да, это можно не соблюдать
без вреда, но от этого тоже можно умереть или хотя бы получить штраф

(На что указывал мой предыдущий пример кода, так это то, что даже для tl важно объявить их перед использованием. Существует опция проверки, которая проверяет объявления, но при нормальном использовании мы не хотим таких накладных расходов.)

Я согласен с @EvanAad, что имена - это просто соглашение. Мы можем проверить, что локальные и глобальные присвоения не смешиваются даже без соглашения об именах l_ / g_: поскольку код проверки может быть несколько медленным, вполне разумно хранить информацию о том, использовалась ли данная переменная в локальном / глобальном назначение. С проверкой типов ситуация аналогична, за исключением двух пар типов, которые невозможно различить (я не собираюсь говорить, какие именно, чтобы не сорвать разговор).

В `` соглашениях '' \amountPaid - это команда документа, а \l_amount_paid_int - нет, а последняя часть пространства имен amount (по соглашению, но довольно важная в TeX ). Это не относится к \l_ / \g_ , хотя я думаю, что во многих языках есть строгий «руль» по именованию без принудительного применения на техническом уровне.

19 октября 2017 года в 16:58 Джозеф Райт [email protected] написал:

По «соглашениям» amountPaid - это команда документа, а \ l_amount_paid_int - нет, а последняя часть пространства имен amount (по соглашению, но довольно важная в TeX). Это не относится к \ l _ / \ g_, хотя я думаю, что во многих языках есть строгий «руль» по именованию, не применяя его на техническом уровне.

Худший кошмар в программировании на LaTeX2e - определение команды
в «пространстве уровня пользователя», скажем, \ foo, чтобы обнаружить, что он конфликтует
с помощью команды другой пакет, определенный для _внутреннего_ использования (так
не задокументировано в руководстве).

Это действительно случилось? Да и не раз. Придерживаясь
\Соглашение @commandname очень помогло избежать
такие проблемы.

Конечно, конфликты на «уровне пользователя» тоже могут произойти,
но их гораздо легче обнаружить и решить. Когда дело доходит до
внутренности, часто бывает необходимо преследовать расширения в очень
глубокий уровень.

В отличие от законодателей, мы не можем налагать штрафы или сажать в тюрьму.
кто не придерживается законов программирования LaTeX3. Но мы
сообщество, и все должны.

Наши рекомендации по соглашениям об именах должны помочь никогда не найти
внутренние команды пакета конфликтуют с командами других.

По личному коду можно делать все, что угодно: есть
нет закона, запрещающего нарушать частную собственность, но
есть один о том, как делать это на дорогах общего пользования.

Если вы хотите локально определить переменную \ f любого типа,
вы должны беспокоиться о том, что команда определяется некоторыми
пакет и, по закону Мерфи, заканчивая точно аргументом
в функцию, использующую переменную \ f. Вы можете представить себе хуже
сценарий?

Чао
Энрико

и вам не нужно называть его \ l_amount_paid_int, вы можете просто назвать его amount_paid, как на любом другом языке программирования. \ L _... - хорошая мнемоника, чтобы напомнить вам, что она должна использоваться локально, но не должна использовать это или любую другую мнемонику.

Конечно. Вы также можете использовать md5-fc693aa157832059d7daeeb61c55 cddb: оплачено или количество и оплачено (это не шутка, я знаю пакет, который использует &) или что вам больше нравится. Но даже если имена являются всего лишь условным обозначением: если люди будут придерживаться таких условностей, общение будет проще. Вы задавали довольно много вопросов о tex.sx. Что вы будете делать, если у вас возникнут проблемы с кодом, написанным в «личном стиле»? Перевести в стандартный стиль, задать вопрос и перевести обратно? Или ожидаете, что каждый сможет понять ваш личный стиль?

@ eg9

По личному коду можно делать все, что угодно

Вы не узнаете этого, читая документацию, это все, что я говорю.

Если вы хотите локально определить переменную \f любого типа, вам следует беспокоиться о том, что команда определяется каким-либо пакетом.

Я не согласен. Если ваш код находится внутри \group_begin: ... \group_end: , и если вы определяете все локальные переменные с помощью \<module>_clear_new:N , и если вы назначаете только локальные переменные с помощью \<module>_set:N... , вам не нужно беспокоиться о конфликты имен ваших локальных переменных с другими пакетами, если ваш код не использует другой пакет.

@ u-fischer

Но даже если имена являются всего лишь условным обозначением: если люди будут придерживаться таких условностей, общение будет проще.

Я не говорю, что нет веских причин придерживаться условностей. Все, что я говорю, это то, что эти соглашения не должны превращаться в языковые правила, и документация должна четко различать соглашения, для которых должно быть изложено логическое обоснование, и правила. И выбор, следовать ли соглашениям, должен в конечном итоге оставаться за программистом.

@EvanAad Вам действительно нужно беспокоиться о конфликтах имен, даже если вы описываете. Скажите, что вы пишете

\cs_new_protected:Npn \evanaad_halve:n #1
  {
    \group_begin:
      \int_zero_new:N \f
      \int_set:Nn \f { (#1) / 2 }
      \iow_term:x { \int_use:N \f }
    \group_end:
  }

тогда пользователь вашего пакета делает

 \int_const:Nn \f {123}
 \evenaad_halve:n { \f }

Они будут удивлены, увидев 0, а не 62.

С другой стороны, если вы придерживаетесь таких имен, как \evanaad_f т. Д. (Или короче \@@_f т. Д., Используя магию l3docstrip ), вы должны быть в безопасности.

Вы не узнаете этого, читая документацию, это все, что я говорю.

Извините, но в файлеexplox3.pdf слово «конвенция» употребляется примерно 20 раз. В случае публичной и частной команды есть даже предложение: «Нет (почти) способа обеспечить это без серьезных вычислительных затрат, поэтому мы реализуем это только через соглашение об именах».

@blefloch Хорошее замечание.

@ u-fischer Хорошо, честно. Как насчет соглашения о том, что имя функции должно заканчиваться спецификатором аргумента? Это не совсем соглашение, потому что функции раздела 3.3 проверяют спецификатор аргумента, но эти функции являются просто «синтаксическим сахаром», и если вы их не используете, нет никаких препятствий для использования таких имен функций, как \mymodule_myfunc , но вы не узнаете этого из руководства.

19 октября 2017 года в 17:52 EvanAad [email protected] написал:

@ u-fischer Хорошо, честно. Как насчет соглашения о том, что имена функций должны заканчиваться спецификатором аргумента? Это не совсем соглашение, потому что функции раздела 3.3 проверяют спецификатор аргумента, но эти функции являются просто «синтаксическим сахаром», и если вы их не используете, нет никаких препятствий для использования таких имен функций, как \ mymodule_myfunc.

Это необходимо для варианта cs_generate_

Чао
Энрико

С \mymodule_myfunc вы не сможете использовать какие-либо функции из
l3expan . Эти функции расширения и понятие вариантов являются
основная часть Exp3.

Хотя я согласен с тем, что имена переменных можно сделать короче (удаление
"l _" / "g_" и "_int" / ...), сигнатура функции на самом деле не "просто
соглашение".

@blefloch Я понимаю вашу точку зрения, и она хорошая, но все же, на мой взгляд, если все, что вам нужно, это написать команду документа, вы должны знать, что вы можете сделать это, определив такую ​​функцию, как

\ExplSyntaxOn
\cs_new:Npn \MyDocumentCommand {Hello,~world!}
\ExplSyntaxOff

и вам не нужно сначала определять «теневую» функцию \mymodule_my_document_command: , а затем копировать ее с помощью

\cs_new_eq:NN \MyDocumentCommand \mymodule_my_document_command:

@blefloch И, между прочим, помимо \cs_generate_variant:Nn , о котором упоминалось в @ eg9 , используют ли какие-либо другие функции модуля l3expan спецификатор аргумента в имени функции?

мне сейчас кажется, что вы в основном спорите ради спора

да, вы можете сделать все это, и к концу дня единственный трудный язык
правила - это то, что движок TeX жестко закрепляет в своих примитивах. И учитывая
что TeX - это самомодифицирующийся язык, с которым вы можете работать в любом месте
там например

\ endlinechar-1 \ def ~ # 1 {\ catcode` # 113} ~ Q ~ S ~ U ~ _ ~ V ~ W ~ J ~ K ~ L ~ M ~ N ~ O ~ @ ~ X ~ Y ~ [~] ~ (
~ | ~ & ~ Z ~ '~ "~ ~ h ~ z ~: ~ q ~ j ~ k ~; ~ / ~) ~! ~, ~ $ ~ + \ Let_ \ let_ ~ newcount ~ $$ - 1 ~ Q ~ J ~ V ~ S~ K ~ W ~ U ~ L ~, ~ '' 1 ~ "" 2 ~ * 1 _ & \ count & 144 '& 155' & 145 "& 154" _ [\ ifnum _ (\ ifcase_O \ or
_ | \ else _] \ fi_N \ number _ @ \ advance_X \ expandafter_Z \ global_Y \ typeout_ ~ newif
~ \ ifG ~ \ if_ ~ \ def ~ j {[0 Q [0Jk | $] | $] | $] | $]} ~ k {& 1NQNJ} ~ \ 2 # 1 # 2 {} ~: # 1 {

11 # 12 # 13 # 14 # 15 # 16 # 17 # 18} ~ h # 1 # 2 {# 2: {~ \ q # 1} ~ # 2 ^^ J} ~ \ q # 1 # 2 {(& 1 # 1 # 2 ~~ OO $]}

~ / {Y {Строка и столбец? например, E6} \ read $ toM \ ifcat ~ X \ 2M ~ $$ X \ jM |! input!]} ~! # 1! {
Y {Неверный # 1.} /} ~ \ J # 1 # 2 {Q #1@Q- @J #2@J- 0; (V! Move!]} ~; {V0 (jS1z1z0z {$} S
0z1z {$} S $ z1z0z {$}]} ~ _ {@, \ ifodd '-]} ~ z # 1 {{\ trueK # 1 {\ falseq}}} ~ q {@ QS @ JK [j = "
\ ifZk'Z_2] @ V1q | [j = 'ZVV \ ifG \ if | \ aftergroupq]]]]} ~ \, # 1 {Q # 1:.} ~. # 1 {J # 1; [0
WWVUQLJ]]} ~ + # 1 {(# 1O2O-2O0O0O0O0O-2O2]} ~) {'X "X" N'Y {^^ J:
~ ^^ Jh1Ah2Bh3Ch4Dh5Eh6Fh7Gh8H: ~ ^^ J} \ GfalseW (W $ | 0] ~: \, \ Gtrue [0 /]; k'_1] [$ = WY {(, Tie | Player [0> ,. | $] ~ выигрывает с счетом N [0>, -],].} X \ dump])} ~~ {})

который представляет собой красивый документ TeX (на самом деле красивый документ LaTeX)
но что касается его кода, он совсем не очень полезен. А также
тот факт, что Бруно может написать такой документ, не означает, что
в руководстве по Exp3 (или в данном случае в руководстве по LaTeX) все должно быть описано.

Код LaTeX (2.09, а также 2e) имел большую проблему, которая в первые дни
слишком многие программисты понимали низкоуровневые ярлыки
в TeX и использовали и неправильно использовали их, потому что они думали, что это не
вредный. В результате огромное количество существующих пакетов 2e
несовместимы друг с другом или имеют небольшие проблемы в некоторых местах при использовании
вместе и т. д. или иногда ломаются, потому что они обошли один или
пользовательский интерфейс (потому что, казалось, он работал без него).

Вы в основном просите нас снова и снова задокументировать именно это,
т. е. возможные ярлыки, которые нарушают принципы проектирования, просто
потому что они иногда работают (а то и в данный момент всегда). Но экспл3
и его условности / правила во многом основаны на опыте того, что
кодеры не подчинялись таким правилам в прошлом, и беспорядок, который возник в результате
из этого. Так что нет, правила преднамеренные, а не просто прихоти (большинство
времени) и даже несмотря на то, что «если вы знаете, что делаете, то вы
может обойти большинство из них в конкретной ситуации ", что не означает, что
если вы переместите свой код из одного места в другое, которое все еще будет
так или должно быть с течением времени.

По словам других, существует большая разница между кодом, который вы пишете.
муха для себя и код, который вы пишете как "официально" распространяемый
упаковка. По крайней мере, для последнего, мы просим принять правила и
считайте их частью языка. Для себя вы можете захотеть
узнать, как кодировать документ, как указано выше, но знать, как это сделать
это не выйдет из руководства Exp3


Сказав это, я не хочу отговаривать вас от сложных концепций,
команды, интерфейсы, что у вас есть. Многие из вопросов, которые вы подняли
другие случаи были хорошо восприняты (или, по крайней мере, заставили нас переосмыслить один
или другой момент).

Но что касается руководства по Exp3, я думаю, что вы слышали от
некоторые люди считают, что нет никакого интереса в документировании
«не-условности» и «не-правила». Более того, если код слишком сильно отвлекает
из того, что мы называем правилами / соглашениями, тогда это все равно будет код TeX, но
больше не код Exp3.

Думаю, мы обсудили это: я закрою, но, конечно, открою снова, если потребуется.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги