如何使 \NewDocumentCommand 成为全局的?

如何使 \NewDocumentCommand 成为全局的?

我正在使用 Christian Hupfer 建议的自定义宏这里创建戏剧剧本排版角色:

\makeatletter
    \NewDocumentCommand{\NewPerson}{m}{%
      \expandafter\NewDocumentCommand\csname #1x\endcsname{+m}{%
        #1: \textbf{##1}\par%
      }
      \expandafter\NewDocumentCommand\csname #1h\endcsname{}{%
        \textsc{#1}%
      }
    }
\makeatother

当像这样调用时:

\NewPerson{thomas}

一切正常,宏\thomash\thomasx可用于整个文档。但是当从组内调用时,如下所示:

{
\NewPerson{thomas}
}

这两个宏只能在组内访问。我该如何定义这些宏,使它们可以在组外使用?我读过关于在\global命令定义前面添加的内容\NewPerson,但这并不能解决问题。MWE 可以是:

\documentclass{minimal}
\usepackage{xparse}

\makeatletter
    \NewDocumentCommand{\NewPerson}{m}{%
      \expandafter\NewDocumentCommand\csname #1x\endcsname{+m}{%
        #1: \textbf{##1}\par%
      }
      \expandafter\NewDocumentCommand\csname #1h\endcsname{}{%
        \textsc{#1}%
      }
    }
\makeatother

\begin{document}
\NewPerson{thomas}
\thomasx{asdf}
\thomash

{
\NewPerson{elvis}
\elvisx{asdf}
\elvish
}

\elvisx{asdf}
\elvish

\end{document}

在这里,使用组外的\elvisx\elvish不起作用。任何关于如何解决这个问题的想法都非常感谢!

答案1

要么使用\expandafter\gdef\csname #1x\endcsname##1{...},要么留在expl3作为依据的领域xparse,说\cs_gset:cpn

为了防止域中空格等的吞噬\ExplSyntaxOn...\ExplSyntaxOff,我定义了格式包装器(或“钩子”),如果以后需要更改语音等格式的风格,这是一个很好的方法。

\documentclass{minimal}
\usepackage{xparse}

\NewDocumentCommand{\personspeechformater}{m+m}{%
  #1: \textbf{#2}\par%
}

\NewDocumentCommand{\personhighlighter}{m}{%
  \textsc{#1}%
}

\ExplSyntaxOn
\NewDocumentCommand{\NewPerson}{m}{%
  \cs_gset:cpn  {#1x} ##1{\personspeechformater{#1}{##1}}%
  \cs_gset:cpn  {#1h} {\personhighlighter{#1}}%
}
\ExplSyntaxOff

% With \gdef



\NewDocumentCommand{\NewOtherPerson}{m}{%
  \expandafter\gdef\csname #1x\endcsname##1{\personspeechformater{#1}{##1}}%
  \expandafter\gdef\csname #1h\endcsname{\personhighlighter{#1}}%
}

\begin{document}
\NewPerson{thomas}
\thomasx{asdf}
\thomash

{
  \NewPerson{elvis}
  \elvisx{asdf}
  \elvish
}


\elvisx{asdf}
\elvish


{
  \NewOtherPerson{Gandalf}
}


\Gandalfx{Foo}
\Gandalfh
\end{document}

在此处输入图片描述

答案2

为了扩展和改进 Christian 的优秀答案,我建议添加一些内容。角色的名字可能有变音符号,这使得无法直接在命令名称中使用该名称。

我还添加了一种跟踪名称的基本方法。

请注意,它\cs_new:Npn会全局起作用,并检查命令是否尚未定义。

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{xparse}

\ExplSyntaxOn

\seq_new:N \g_lukelr_persons_seq
\prop_new:N \g_lukelr_persons_prop

\NewDocumentCommand{\NewPerson}{O{#2}m}
 {
  \cs_new:cpn {#1x} ##1 { \speech { #2 } { ##1 } }
  \cs_new:cpn {#1h} { \mention { #2 } }
  % list of keys for persons
  \seq_gput_right:Nn \g_lukelr_persons_seq { #1 }
  % correspondence between keys and persons
  \prop_gput:Nnn \g_lukelr_persons_prop { #1 } { #2 }
 }

\NewDocumentCommand{\ListPersons}{}
 {
  \seq_map_inline:Nn \g_lukelr_persons_seq
   {
    \prop_item:Nn \g_lukelr_persons_prop { ##1 } \par
   }
 }
\ExplSyntaxOff

\NewDocumentCommand{\speech}{ m +m }{%
  #1: {\bfseries #2}\par
}
\NewDocumentCommand{\mention}{ m }{%
  \textsc{#1}%
}

\begin{document}

\NewPerson{Treemunch}
{\NewPerson[Ooc]{Ööç}} % in a group just for testing

Scene 1: \Treemunchh\ and \Ooch

\Treemunchx{Here I am.}
\Oocx{Why are you so late?}

\bigskip

\ListPersons

\end{document}

在此处输入图片描述

答案3

我编写了一个函数\GNewDocumentCommand,它具有与 相同的签名\NewDocumentCommand和几乎相同的语义,只是新命令是全局定义的。我还为 、 和 变体编写了类似RenewProvide版本Declare

使用这些函数的一种方法是将以下代码粘贴到新文件 中xparse-global.tex,将此文件放在 TeX 引擎的搜索路径中,然后将代码导入到您的工作 TeX 文档中:\input xparse-global。您还必须使用 导入xparse包。在 之前或之后导入\usepackage都没有关系。或者,您可以从此代码创建一个 LaTeX2e 包并使用 导入它。xparsexparse-global\usepackage

\input expl3-generic
\ExplSyntaxOn
\group_begin:

\cs_set_protected:Npn \__xparse_global_new:NN #1#2
{
    \cs_new_protected:Npn #2 ##1##2##3
    {
        #1 ##1 {##2} {##3}
        \cs_gset_eq:NN ##1 ##1

        % The following couple of lines make use of xparse's internals,
        % namely the implementation of \...DocumentCommand's "return value".
        \cs_gset_eq:cc { \cs_to_str:N ##1 ~ } { \cs_to_str:N ##1 ~ }
        \cs_gset_eq:cc { \cs_to_str:N ##1 ~code } { \cs_to_str:N ##1 ~code }
    }
}

\clist_set:Nn \l__variants_clist { New, Renew, Provide, Declare }
\clist_map_variable:NNn \l__variants_clist \l__variant_str
{
    \str_set:Nx \l__local_func_name_str { \l__variant_str DocumentCommand }
    \str_set:Nx \l__global_func_name_str { G \l__local_func_name_str }

    \exp_args:Ncc \__xparse_global_new:NN
        { \l__local_func_name_str }
        { \l__global_func_name_str }
}

\group_end:
\ExplSyntaxOff

我的代码依赖于源代码的实现\NewDocumentCommand及其同级实现xparse,因此如果将来此实现发生变化,它可能会中断。如果 LaTeX3 人员可以将我的代码合并到包中并与此包的未来修改保持同步,那就太好了xparse


现在,您的最小示例可以重写如下。添加了一行,并稍微修改了两行。请参阅下面的注释。

\documentclass{minimal}
\usepackage{xparse}
% This line was added
\input xparse-global

\makeatletter
    \NewDocumentCommand{\NewPerson}{m}{%
      % This line was modified: \NewDocumentCommand -> \GNewDocumentCommand
      \expandafter\GNewDocumentCommand\csname #1x\endcsname{+m}{%
        #1: \textbf{##1}\par%
      }
      % This line was modified: \NewDocumentCommand -> \GNewDocumentCommand
      \expandafter\GNewDocumentCommand\csname #1h\endcsname{}{%
        \textsc{#1}%
      }
    }
\makeatother

\begin{document}
\NewPerson{thomas}
\thomasx{asdf}
\thomash

{
\NewPerson{elvis}
\elvisx{asdf}
\elvish
}

\elvisx{asdf}
\elvish

\end{document}

编译成功,排版如下。

OP 的最小示例用我的全局版本的 exparse 函数重写


PS 您的最小示例包含一个未显现的错误。要修复它,请将其替换+mm,或将其替换\textbf{##1}\begin{bfseries}##1\end{bfseries}

相关内容