如何创建具有两个或三个 ; 分隔参数的宏?

如何创建具有两个或三个 ; 分隔参数的宏?

我想在二维空间中用 创建一个点,\defpoint(1;2){A}在三维空间中用创建一个点\defpoint(1;2;-5){A}。我更喜欢使用 TeX 的解决方案,但看看 LaTeX3 在这种情况下是否有用会很有趣。我从未尝试过用 LaTeX3 编程。

我使用了;和而不是,,因为在法语中我想写:\defpoint(1,5;2,5){A} 而不是 \defpoint({1,5},{2,5}){A}

还有其他条件:

我想要 \defpoint(12;23){A},但又不想\defpoint({12};{23}){A}

代码示例,但它不遵守最后一个条件:

\documentclass{minimal}
\makeatletter
\def\defpoint(#1;#2{\@ifnextchar){\coor@ii(#1;#2}{\coor@iii(#1;#2}}
\def\coor@ii(#1;#2)#3{two coordinates: #1;#2 and #3}
\def\coor@iii(#1;#2;#3)#4{three coordinates: #1;#2;#3 and #4}
\makeatother
\begin{document}

\defpoint(1;2;3){A}

%\defpoint(12;13){B}     % here we need defpoint({12};{13}){B}

\end{document}

下一个条件是一个真正的问题,而且它不是必需的;我想用 \defpoint({sin(45)};{cos(45)}){A}来避免\defpoint(sin(45);cos(45)){A};但我知道这非常困难。使用 TikZ,我们在某些情况下需要使用括号。

评估表达式将会很有趣,一个好的解决方案也应该允许轻松评估

答案1

\documentclass{article}
\makeatletter
\def\defpoint#1#{\expandafter\defpoint@i#1;;\@nil}
\def\defpoint@i#1;#2;#3;#4\@nil#5{%
  \ifx\relax#3\relax \defpoint@ii#1;#2\@nil{#5}\else\defpoint@iii#1;#2;#3\@nil{#5}\fi}
\def\defpoint@ii(#1;#2)\@nil#3{2D~co-ordinates~-~#1;#2,~mandatory~argument~-~'#3'\\}
\def\defpoint@iii(#1;#2;#3)\@nil#4{3D~co-ordinates~-~#1;#2;#3,~mandatory~argument~-~'#4'\\}
\makeatother
\begin{document}
\noindent
\defpoint(1;2){A}
\defpoint(12;13){B} 
\defpoint(sin(45);cos(45)){A}
\defpoint(1;2;-5){A}
\end{document}

在此处输入图片描述

答案2

由于 LaTeX3 解决方案是可以接受的,因此我会使用xparse。 的内部结构xparse可以正确处理嵌套的可选参数括号或类似内容,因此\defpoint(sin(45);cos(45)){A}不是问题。

你没有说是否\defpoint需要可扩展。假设不需要,那么满足条件的解决方案是

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \defpoint
  { > { \SplitArgument { 2 } { ; } } D ( ) { 0 ; 0 } m }
  { \defpoint_aux:nnnn #1 {#2} }
\cs_new_protected:Npn \defpoint_aux:nnnn #1#2#3#4
  {
    \IfNoValueTF {#3}
      { 2D~co-ordinates~-~(#1;#2),~mandatory~argument~-~'#4' \\ }
      { 3D~co-ordinates~-~(#1;#2;#3),~mandatory~argument~-~'#4' \\ }
  }
\ExplSyntaxOff
\begin{document}
\noindent
\defpoint(1;2){A}
\defpoint(12;13){B} 
\defpoint(sin(45);cos(45)){A}
\defpoint(1;2;-5){A}
\end{document}

我需要一个内部函数 ( \defpoint_aux:nnnn) 来处理可变数量的坐标。 结果是\SplitArgument将第一个参数最多分为两个;标记,并且始终会产生三个 <平衡文本#1>。辅助函数会将其作为、#2和拾取#3,因此我们可以测试二维相对3D 通过查看第三个参数是否是特殊\NoValue标记。

我上面没有介绍过,但你也可以测试括号中的参数是否给出,以及它是否只包含一个参数(IE;),再次使用\IfNoValueTF测试。

对于可扩展的方法,您需要做更多的工作,以及最新的副本xparse(嵌套可选参数在我检查这个问题之前不具备可扩展性)。

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand \defpoint
  { D ( ) { 0 ; 0 } m }
  { \defpoint_aux:nn {#1} {#2} }
\cs_new:Npn \defpoint_aux:nn #1#2
  { \defpoint_aux:nw {#2} #1 ; \q_nil ; \q_stop }
\cs_new:Npn \defpoint_aux:nw #1#2 ; #3 ; #4 ; #5 \q_stop
  {
    \quark_if_nil:nTF {#4}
      { \defpoint_aux:nnnn {#2} {#3} { \NoValue } {#1} }
      { \defpoint_aux:nnnn {#2} {#3} {#4} {#1} }
  }
\cs_new:Npn \defpoint_aux:nnnn #1#2#3#4
  {
    \IfNoValueTF {#3}
      { 2D~co-ordinates~-~(#1;#2),~mandatory~argument~-~'#4' \\ }
      { 3D~co-ordinates~-~(#1;#2;#3),~mandatory~argument~-~'#4' \\ }
  }
\ExplSyntaxOff
\begin{document}
\noindent
\defpoint(1;2){A}
\defpoint(12;13){B} 
\defpoint(sin(45);cos(45)){A}
\defpoint(1;2;-5){A}
\end{document}

内部结构的想法与其他人所建议的非常相似,只是我使用预构建测试来测试“夸克”(特殊标记)。同样,我还没有完全测试这里的输入,因此例如空的可选参数会导致问题。

答案3

使用 eTeX,您应该使用\scantokens

\documentclass{article}
\makeatletter
\def\defpoint{%
    \edef\saved@catcode{\number\catcode`\;}%
    \catcode`\;12
    \begingroup
    \catcode`\(1 \catcode`\)2 \catcode`\ 9
    \defpoint@i
}
\def\defpoint@i#1{%
    \endgroup
    \endlinechar-1 \everyeof{\noexpand}%
    \edef\coord@point{\scantokens{#1}}%
    \expandafter\defpoint@ii\coord@point;\@nil
}
\def\defpoint@ii#1;#2;#3\@nil#4{%
    \catcode`\;\saved@catcode\relax
    \ifx\relax#3\relax
        2 coordonn\'ees : #1 et #2
    \else
        3 coordonn\'ees : #1 ; #2 et \def@point@iii#3
    \fi
    puis le point : #4\par
}
\def\def@point@iii#1;{#1}
\makeatother
\begin{document}
\defpoint(12;13){M}
\defpoint( sin(30) ; cos(45) ; 4 ){A}
\end{document}

答案4

您的问题是;#2{

\def\defpoint(#1;#2{\@ifnextchar){\coor@ii(#1;#2}{\coor@iii(#1;#2}}

它告诉将下一个标记或平衡组存储在;as之后#2。如果您不将内容包装在那里,则{ }只会取第一个标记/字符。因此,它仅在#2只有一个字符时才有效,但在一般情况下则无效。您需要读取到的所有内容,)然后测试;其中是否包含 a。


以下代码读取第一个;和之间的所有内容),在其后面添加一个;带有一些的\relax,然后使用第二个宏,该宏使用两个;分隔的部分并以\relax作为结束标记。然后它检查添加的是否;仍然存在,这表明只有一个坐标。在这种情况下,#2直接使用原始内容,因为现在读取的部分(#3)将包含第一个添加的\relax

\documentclass{article}
\makeatletter
\def\defpoint(#1;#2){%
    \@defpoint{#1}{#2}#2\relax;\relax\relax
}
\def\@defpoint#1#2#3;#4\relax#5{%
    \ifx;#5\relax% is it the added the `;` or the trailing `\relax`?
        \def\next{\coor@iii({#1};{#3};{#4})}%
    \else
        \def\next{\coor@ii({#1};{#2})}%
    \fi
    \next
}

\def\coor@ii(#1;#2)#3{two coordinates: #1;#2 and #3}
\def\coor@iii(#1;#2;#3)#4{three coordinates: #1;#2;#3 and #4}
\makeatother
\begin{document}

\defpoint(1;2;3){A}

\defpoint(12;23;34){B}

\defpoint(12;13){C}

\defpoint(1;3){D}

\end{document}

代码不是完全可扩展的,但可以这样写。不过,因为它似乎是一个定义,所以我认为它不是必需的。

相关内容