如何在 LaTeX3 中定义嵌套环境和命令

如何在 LaTeX3 中定义嵌套环境和命令

我想定义嵌套环境,这些环境具有相同的子命令和子环境嵌套定义。使用方法如下:

\begin{myenva}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{}
    \end{myenvz}
  \end{myenvx}
\end{myenva}

\begin{myenvb}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{}
    \end{myenvz}
  \end{myenvx}
\end{myenvb}

我希望myenvx是一个完全不同的环境,在myenva和下使用时具有不同的功能myenvb。同样,我希望myenvz之下与之下myenva/myenvx完全不同。myenvzmyenvb/myenvx

我有以下 MWE 演示我如何尝试使其工作:

\documentclass[a4paper]{article}

\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn

\begin{document}

\NewDocumentEnvironment{myenva}{} {
  \NewDocumentEnvironment{myenvx}{} {
    \NewDocumentEnvironment{myenvz}{} {
      \NewDocumentCommand{mycmdz} {
        \mycmda{}
        \mycmdx{}
        \typeout{mycmdz}
      }
    }{}

    \NewDocumentCommand{mycmdx} {
      \typeout{mycmdx}
    }
  }{}

  \NewDocumentCommand{mycmda} {
    \typeout{mycmda}
  }
}{}

\NewDocumentEnvironment{myenvb}{} {
  \NewDocumentEnvironment{myenvx}{} {
    \NewDocumentEnvironment{myenvz}{} {
      \NewDocumentCommand{mycmdz} {
        \mycmdb{}
        \mycmdx{}
        \typeout{mycmdz}
      }
    }{}

    \NewDocumentCommand{mycmdx} {
      \typeout{mycmdx}
    }
  }{}

  \NewDocumentCommand{mycmdb} {
    \typeout{mycmdb}
  }
}{}

\begin{myenva}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{}
    \end{myenvz}
  \end{myenvx}
\end{myenva}

\begin{myenvb}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{}
    \end{myenvz}
  \end{myenvx}
\end{myenvb}

\end{document}

但是我在尝试编译时收到此错误:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! LaTeX error: "xparse/command-already-defined"
!
! Command 'mycmda' already defined!
!
! See the LaTeX3 documentation for further information.
!
! For immediate help type H <return>.
!...............................................

l.51   \begin
             {myenvx}

我不确定如何准确定义嵌套环境。它曾经在 LaTeX2 中工作\newenvironment,但似乎在 LaTeX3 中不起作用。不确定可能出了什么问题,似乎可能有几件事。目标是能够定义嵌套环境和命令,其中不同环境中的子命令中的名称相同。

(我希望环境的范围像这样,这样就可以用不同的方式处理它们。)

我认为我不应该\DeclareDocumentCommand在任何地方使用。也许我应该在这里使用新的 LaTeX3 函数,但不确定。我可以通过使用 来获得更进一步的结果\cs_new:Npn,如在这个 MWE 中:

\documentclass[a4paper]{article}

\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn

\begin{document}

\NewDocumentEnvironment{myenva}{} {
  \NewDocumentEnvironment{myenvx}{} {
    \NewDocumentEnvironment{myenvz}{} {
      \cs_new:Npn \mycmdz {
        \mycmda{}
        \mycmdx{}
        \typeout{mycmdz}
      }
    }{}

    \cs_new:Npn \mycmdx {
      \typeout{mycmdx}
    }
  }{}

  \cs_new:Npn \mycmda {
    \typeout{mycmda}
  }
}{}

\NewDocumentEnvironment{myenvb}{} {
  \NewDocumentEnvironment{myenvx}{} {
    \NewDocumentEnvironment{myenvz}{} {
      \cs_new:Npn \mycmdz {
        \mycmda{}
        \mycmdx{}
        \typeout{mycmdz}
      }
    }{}

    \cs_new:Npn \mycmdx {
      \typeout{mycmdx}
    }
  }{}

  \cs_new:Npn \mycmdb {
    \typeout{mycmdb}
  }
}{}

\begin{myenva}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{}
    \end{myenvz}
  \end{myenvx}
\end{myenva}

\begin{myenvb}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{}
    \end{myenvz}
  \end{myenvx}
\end{myenvb}

\end{document}

但仍然出错:

mycmda
mycmdx
mycmdz

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! LaTeX error: "kernel/command-already-defined"
!
! Control sequence \mycmdx already defined.
!
! See the LaTeX3 documentation for further information.
!
! For immediate help type H <return>.
!...............................................

使用 LaTeX2 功能时,我能够实现所需的行为,如以下 MWE 所示:

\documentclass[a4paper]{article}

\usepackage{expl3}
\usepackage{xparse}

\begin{document}

\newenvironment{myenva} {
  \newenvironment{myenvx} {
    \newenvironment{myenvz} {
      \newcommand{\mycmdz} {
        \mycmda{}
        \mycmdx{}
        \typeout{mycmdz}
      }
    }{}

    \newcommand{\mycmdx} {
      \typeout{mycmdx}
    }
  }{}

  \newcommand{\mycmda} {
    \typeout{mycmda}
  }
}{}

\newenvironment{myenvb} {
  \newenvironment{myenvx} {
    \newenvironment{myenvz} {
      \newcommand{\mycmdz} {
        \mycmdb{}
        \mycmdx{}
        \typeout{mycmdz}
      }
    }{}

    \newcommand{\mycmdx} {
      \typeout{mycmdx}
    }
  }{}

  \newcommand{\mycmdb} {
    \typeout{mycmdb}
  }
}{}

\begin{myenva}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{}
    \end{myenvz}
  \end{myenvx}
\end{myenva}

\begin{myenvb}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{}
    \end{myenvz}
  \end{myenvx}
\end{myenvb}

\end{document}

但是,我想在完成嵌套环境/命令时利用以下功能:

  1. 使用 LaTeX3 功能。
  2. 得益于 ,可以进行动态/重载参数解析xparse

更新

我尝试了更多的排列以确保它涵盖了我所寻找的情况,并且一切似乎都有效,耶!

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn


\NewDocumentEnvironment{myenvx}{}{}{}
\NewDocumentEnvironment{myenvz}{}{}{}

\NewDocumentCommand{\mycmdz}{}{}

\NewDocumentEnvironment{myenva}{}{%
  \NewDocumentCommand{\mycmda}{mm}{%
    \typeout{mycmda}
    \typeout{##1}
    \typeout{##2}
  }%
  \RenewDocumentEnvironment{myenvx}{}{%
    \NewDocumentCommand{\mycmdx}{mm}{%
      \typeout{mycmdx}
      \typeout{####1}
      \typeout{####2}
    }%
    \RenewDocumentEnvironment{myenvz}{}{%
      \RenewDocumentCommand{\mycmdz}{mm}{%
        \mycmda{mycmda:a}{mycmda:b}
        \mycmdx{mycmda:mycmdx:a}{mycmda:mycmdx:b}
        \typeout{mycmdz}
        \typeout{########1}
        \typeout{########2}
      }%
    }{}%
    %
  }{}%
 %
}{}

\NewDocumentEnvironment{myenvb}{} {%
  \NewDocumentCommand{\mycmdb}{}{%
    \typeout{mycmdb}
  }%
  \RenewDocumentEnvironment{myenvx}{} {%
    \NewDocumentCommand{\mycmdx}{mmm}{%
      \typeout{mycmdx}
      \typeout{####1}
      \typeout{####2}
      \typeout{####3}
    }%
    \RenewDocumentEnvironment{myenvz}{} {%
      \RenewDocumentCommand{\mycmdz}{mmm}{%
        \mycmdb{}
        \mycmdx{mycmdb:mycmdx:a}{mycmdb:mycmdx:b}{mycmdb:mycmdx:c}
        \typeout{mycmdz}
        \typeout{########1}
        \typeout{########2}
        \typeout{########3}
      }%
    }{}%
    %
  }{}%
  %
}{}

\begin{document}
\begin{myenva}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{mycmda:mycmdx:mycmdz:a}{mycmda:mycmdx:mycmdz:b}
    \end{myenvz}
  \end{myenvx}
\end{myenva}

\begin{myenvb}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{mycmdb:mycmdx:mycmdz:a}{mycmdb:mycmdx:mycmdz:b}{mycmdb:mycmdx:mycmdz:c}
    \end{myenvz}
  \end{myenvx}
\end{myenvb}

\begin{myenva}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{mycmda:mycmdx:mycmdz:a}{mycmda:mycmdx:mycmdz:b}
    \end{myenvz}
  \end{myenvx}
\end{myenva}
\end{document}

输出:

mycmda
mycmda:a
mycmda:b
mycmdx
mycmda:mycmdx:a
mycmda:mycmdx:b
mycmdz
mycmda:mycmdx:mycmdz:a
mycmda:mycmdx:mycmdz:b
mycmdb
mycmdx
mycmdb:mycmdx:a
mycmdb:mycmdx:b
mycmdb:mycmdx:c
mycmdz
mycmdb:mycmdx:mycmdz:a
mycmdb:mycmdx:mycmdz:b
mycmdb:mycmdx:mycmdz:c
mycmda
mycmda:a
mycmda:b
mycmdx
mycmda:mycmdx:a
mycmda:mycmdx:b
mycmdz
mycmda:mycmdx:mycmdz:a
mycmda:mycmdx:mycmdz:b

更新 2

似乎没有RenewDocumentCommand和也可以工作RenewDocumentEnvironment

\documentclass[a4paper]{article}

\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentEnvironment{myenva}{}{
  \NewDocumentCommand{\mycmda}{mm}{
    \typeout{mycmda}
    \typeout{##1}
    \typeout{##2}
  }
  \NewDocumentEnvironment{myenvx}{}{
    \NewDocumentCommand{\mycmdx}{mm}{
      \typeout{mycmdx}
      \typeout{####1}
      \typeout{####2}
    }
    \NewDocumentEnvironment{myenvz}{}{
      \NewDocumentCommand{\mycmdz}{mm}{
        \mycmda{mycmda:a}{mycmda:b}
        \mycmdx{mycmda:mycmdx:a}{mycmda:mycmdx:b}
        \typeout{mycmdz}
        \typeout{########1}
        \typeout{########2}
      }
    }{}

  }{}

}{}

\NewDocumentEnvironment{myenvb}{} {
  \NewDocumentCommand{\mycmdb}{}{
    \typeout{mycmdb}
  }
  \NewDocumentEnvironment{myenvx}{} {
    \NewDocumentCommand{\mycmdx}{mmm}{
      \typeout{mycmdx}
      \typeout{####1}
      \typeout{####2}
      \typeout{####3}
    }
    \NewDocumentEnvironment{myenvz}{} {
      \NewDocumentCommand{\mycmdz}{mmm}{
        \mycmdb{}
        \mycmdx{mycmdb:mycmdx:a}{mycmdb:mycmdx:b}{mycmdb:mycmdx:c}
        \typeout{mycmdz}
        \typeout{########1}
        \typeout{########2}
        \typeout{########3}
      }
    }{}

  }{}

}{}

\begin{document}
\begin{myenva}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{mycmda:mycmdx:mycmdz:a}{mycmda:mycmdx:mycmdz:b}
    \end{myenvz}
  \end{myenvx}
\end{myenva}

\begin{myenvb}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{mycmdb:mycmdx:mycmdz:a}{mycmdb:mycmdx:mycmdz:b}{mycmdb:mycmdx:mycmdz:c}
    \end{myenvz}
  \end{myenvx}
\end{myenvb}

\begin{myenva}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{mycmda:mycmdx:mycmdz:a}{mycmda:mycmdx:mycmdz:b}
    \end{myenvz}
  \end{myenvx}
\end{myenva}
\end{document}

答案1

我认为这就是你所需要的:

enter image description here

##笔记:

  • 首先定义每个环境/宏,然后根据需要重新定义它们。在这种情况下,定义似乎只是本地的,因此您不一定需要先定义它们然后重新定义它们。我倾向于始终先定义它们然后重新定义它们,以确保不会在其他地方使用相同的名称。更新定义只是检查名称是否已定义,它不会检查参数规范是否匹配,因此您可以根据需要自由地在本地更改它们。

  • 宏名称以斜杠开头\,环境名称则不然。

  • \NewDocumentEnvironment/\RenewDocumentCommand需要参数规范,因此如果没有参数(如这里的情况),您必须指定一个空的参数规范{}。您的 MWE 中缺少这些。因此,一般格式是

      \NewDocumentCommand{<macro name starting with a slash>}{<arg spec>}{<body>}
    
  • 类似地,\NewDocumentEnvironment/\RenewDocumentEnvironment也需要参数规范。您的 MWE 中缺少这些。因此,一般格式为

      \NewDocumentEnvironment{<env name without leading slash>}{<arg spec>}{<begin env code>}{<end env code>}
    
  • 当你想要一个宏时仅有的被定义为之内另一个环境/宏,你应该定义它使用它。因此,我将这些定义上移。

  • 另外,要小心虚假的空格。请参阅此Tex 容量超出范围(如果在使用宏后删除%)作为如果不这样做会发生什么情况的一个例子。

由于我不能 100% 确定哪些地方不需要,我往往会添加比实际需要更多的内容。然而,我因过度使用而受到责备在 \newcommand 或类似命令中,在行尾添加百分号什么时候有害。不过,我也自动添加\relax以下任何类型\nuxper\dimexpr所以没有然而遇到过度使用造成的任何问题(但我不推荐这样做)。

  • 当遇到问题时,我的建议是,在尝试更复杂的事情之前,先让初始版本尽可能简单。例如,你可以先从单身的(非嵌套)宏定义,以确保正确无误。这会显示缺少参数规范和宏名称的问题。然后单身的(非嵌套)环境,以确保正确无误。然后从那里构建嵌套环境(只需从两个级别开始,没有必要从三个级别开始)。

  • 如果嵌套的宏/环境有参数,要访问它们,您需要双重使用#。有关更多信息,请参阅:

  • 在 newcommand 中更新命令

  • 在其他命令中定义 LaTeX 命令

  • 嵌套命令定义超过两层

  • 为什么 \renewcommand 的参数需要在 \foreach 中重复 #

  • 据我所知,使用\ExplSyntaxOn可以省略%,但宏中的文本中的任何空格也将被删除。您可以自行决定如何处理空格:在定义中,或在文本中。

##代码:

\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}

\NewDocumentEnvironment{myenvx}{}{}{}
\NewDocumentEnvironment{myenvz}{}{}{}

\NewDocumentCommand{\mycmdz}{}{}

\NewDocumentEnvironment{myenva}{}{%
  \NewDocumentCommand{\mycmda}{}{%
    \typeout{mycmda}
  }%
  \RenewDocumentEnvironment{myenvx}{}{%
    \NewDocumentCommand{\mycmdx}{}{%
      \typeout{mycmdx}
    }%
    \RenewDocumentEnvironment{myenvz}{}{%
      \RenewDocumentCommand{\mycmdz}{}{%
        \mycmda{}
        \mycmdx{}
        \typeout{mycmdz}
      }%
    }{}%
    %
  }{}%
 %
}{}

\NewDocumentEnvironment{myenvb}{} {%
  \NewDocumentCommand{\mycmdb}{}{%
    \typeout{mycmdb}
  }%
  \RenewDocumentEnvironment{myenvx}{} {%
    \NewDocumentCommand{\mycmdx}{}{%
      \typeout{mycmdx}
    }%
    \RenewDocumentEnvironment{myenvz}{} {%
      \RenewDocumentCommand{\mycmdz}{}{%
        \mycmdb{}
        \mycmdx{}
        \typeout{mycmdz}
      }%
    }{}%
    %
  }{}%
  %
}{}

\begin{document}
\begin{myenva}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{}
    \end{myenvz}
  \end{myenvx}
\end{myenva}

\begin{myenvb}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz{}
    \end{myenvz}
  \end{myenvx}
\end{myenvb}
\end{document}

答案2

让内部命令适应其上下文通常比让外部命令重新定义一切更简单。

这里我只是输入,制作

myenva:myenvx:myenvz:mycmdz
myenvb:myenvx:myenvz:mycmdz

但给定这些不同的序列,\mycmdz可以执行任意不同的代码。

\documentclass[a4paper]{article}

\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn
\makeatletter
\cs_new:Nn\colonify:n{#1:}

\seq_new:N\zz_seq{}

\NewDocumentEnvironment{myenva}{}{\seq_put_right:Nx\zz_seq{\@currenvir}} {}
\NewDocumentEnvironment{myenvb}{}{\seq_put_right:Nx\zz_seq{\@currenvir}} {}
\NewDocumentEnvironment{myenvx}{}{\seq_put_right:Nx\zz_seq{\@currenvir}} {}
\NewDocumentEnvironment{myenvz}{}{\seq_put_right:Nx\zz_seq{\@currenvir}} {}

\NewDocumentCommand\mycmdz{}{
\typeout{
  \seq_map_function:NN\zz_seq\colonify:n mycmdz
  }}

\ExplSyntaxOff

\begin{document}

\begin{myenva}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz
    \end{myenvz}
  \end{myenvx}
\end{myenva}

\begin{myenvb}
  \begin{myenvx}
    \begin{myenvz}
      \mycmdz
    \end{myenvz}
  \end{myenvx}
\end{myenvb}

\end{document}

相关内容