expl3
включает спецификаторы аргумента o
, f
, и x
для одноуровневого и двух видов полного расширения соответственно, перед передачей аргумента базовой функции. Какой предпочтительный способ расширения аргументадваждыперед передачей его базовой функции?
В качестве примера я пытаюсь написать l3keys
интерфейс «ключ-значение» для макросов, созданных с помощью \DeclarePairedDelimiter
пакета mathtools
.
\tl_new:N \l__mymodule_size_tl
\keys_define:nn { mymodule }
{
size .choices:nn =
{ big , Big , bigg , Bigg }
{
\tl_set:Nn \l__mymodule_size_tl
{ [ \use:c { \tl_use:N \l_keys_choice_tl } ] }
} ,
size / auto .code:n =
\tl_set:Nn \l__mymodule_size_tl {*} ,
size / none .code:n =
\tl_clear:N \l__mymodule_size_tl
}
Обычно размер разделителей передается макросу как необязательный аргумент \big
etc. или *
, например, \abs[\big]{x}
where \DeclarePairedDelimiter\abs{\lvert}{\rvert}
. Я бы предпочел иметь size = big
etc. или size = auto
как интерфейс ключ-значение, например, \myabs[size=big]{x}
.
Проблема с приведенным выше кодом заключается в правильной настройке \l__mymodule_size_tl
в big
параметрах etc.: мне нужно расширить[ \use:c { \tl_use:N \l_keys_choice_tl } ]
дваждыперед назначением этих токенов \l__mymodule_size_tl
.
- Если я не использую расширение (т. е.
\tl_set:Nn
), вызов\abs
получит свой аргумент в неправильной форме —\l__mymodule_size_tl
будет содержать[ \use:c { \tl_use:N \l_keys_choice_tl } ]
, а не[\big]
. - Если я использую одноуровневое расширение (т. е.
\tl_set:No
) и добавлю его[
после завершения расширения,\l__mymodule_size_tl
то будет храниться[ \cs:w \tl_use:N \l_keys_choice_tl \cs_end: ]
, а не[\big]
. - Если я использую расширение
f
илиx
,\l__mymodule_size_tl
будет храниться куча «мусора», а не[\big]
.
Что я могу сделать? Вот идея: использовать два o
расширения, с некоторой акробатикой между ними.
\tl_set:No \l__mymodule_size_tl
{ \use:c { \tl_use:N \l_keys_choice_tl } ] }
\exp_args:NNV \tl_set:No \l__mymodule_size_tl
\l__mymodule_size_tl
\tl_put_left:Nn \l__mymodule_size_tl { [ }
По-моему, это довольно некрасиво. Можно ли сделать что-то получше?
решение1
В былые времена существовал d
аргумент типа - для двойного расширения. Однако несколько лет назад он был отброшен, поскольку было очень мало мест, где он требовался, особенно когда команда решила toks
вообще не использовать регистры. По памяти, в expl3
двух местах нам пришлось использовать конструкцию \exp_args:NNo \exp_args:No
или подобную форму.
Причина, по которой мы не сохранили d
аргументы -type, двояка. Во-первых, мы хотим избежать, где это возможно, опоры на знание деталей расширения функций: это требуется для низкоуровневой работы, но в идеале должно быть относительно ограничено. Другая причина в том, что это требуется очень редко, поскольку у нас есть e-TeX. Главное помнить, что это позволяет нам контролировать расширение в x
контекстах -type
\tl_set:Nx \l__mymodule_size_tl
{ [ \exp_not:c { \tl_use:N \l_keys_choice_tl } ] }
Хотя здесь это не требуется, мы документируем \cs:w
... \cs_end:
для ситуаций, когда требуется только одно расширение, поэтому вы можете иметь
\tl_set:No \l__mymodule_size_tl
{ \exp_after:wN [ \cs:w \tl_use:N \l_keys_choice_tl \cs_end: ] }
хотя я бы не стал использовать это здесь.
решение2
У меня сложилось впечатление, что вы хотите сделать
\tl_set:Nx \l__mymodule_size_tl
{
[ \exp_not:c { \tl_use:N \l_keys_choice_tl } ]
}
При срабатывании x
расширения \exp_not:c
аргумент полностью расширяется, как всегда, когда c
задействован (это \csname...\endcsname
); затем результирующий токен становится нерасширяемым, поэтому если у вас есть
size=big
вы попадете [\big]
в свой список токенов.