带参数的新命令的嵌套内联数学运算

带参数的新命令的嵌套内联数学运算

人们一致认为不应该$...$使用\(...\)\ensuremath 里面newcommand(或,...)定义来NewDocumentCommand包装在数学模式下排版的传递的宏参数:

\documentclass{article}

\begin{document}
\newcommand*{\somecommand}[1]{
    \(#1\)
}
\somecommand{Hello!}
\end{document}

这里,Hello!将被排版为数学,但该宏的用户/作者可能永远不知道是什么击中了他们。这留下了一些不足之处,可能会让用户感到非常惊讶。它应该可以工作,例如,像\somecommand{$Hello!$},其中\newcommand定义省略进入数学模式。作者应该熟悉并了解数学和文本模式。

现在,我有一个命令应该结合宏参数输入更多的数学,在本例中是一个等号:

\documentclass{article}

\begin{document}
\newcommand*{\somecommand}[1]{
    #1\(=\)
}
\somecommand{\(A\)}
\end{document}

但这会破坏数学间距:

数学间隔

我们实际上有\(A\)\(=\)。这没有什么意义。我们不能定义\somecommad为有\(#1=\):当#1只是文本时这有效,但如果它已经是数学模式(LaTeX Error: Bad math environment delimiter)则不行。这种情况将扩展为\(\(A\) = \)

问题是:为什么这不起作用?

\(over的优点$是可以匹配左右分隔符。从\ensuremath上面链接的例子中,很明显为什么等效的 with$不起作用:$ $A$ = $是合法语法($$A$ = $不是),但完全没用(A在文本模式下)。为什么嵌套 LaTeX 内联数学分隔符\(不起作用?它可以忽略任何内部\(...\)对。从技术上讲,它没有理由混淆(与 相对$)(?)。

为了解决上述问题(有一个附加等号/一些更多的数学输入),似乎只有两种解决方案,而且都很糟糕:

  1. #1\(=\),破坏间距并需要人工干预\,
  2. \(#1=\)。间距有效,但输入参数处于文本模式。这对于 IDE、linters 等来说非常糟糕(会将数学模式字符(比如下划线)标记为错误,因为它们将在数学模式之外使用)。

编辑:要求提供有关该问题的更多背景信息。该上下文相当长;我试图将其保持在可用的最短范围内。

我正在根据课程创建考试模板exam。格式要求如下:

  1. 页面顶部有该页面的问题。
  2. 下面是一个方框,用于填写该问题的答案(或部分答案)。答案的类型可以灵活(通常是一组所需的方程式或一幅画)。
  3. 在那个大盒子下面,可能未知其他框的数量(0、1、...)用于进一步划分学生的答案。
  4. 所有这些应该自动垂直填充当前页面。
  5. 最后,考试结束后,将提供一份解答表以供将来参考。答案应该,感谢exam功能,答案应该可以切换并“自动”填写。这是引出上述问题的核心问题(调用者应该在数学模式下输入参数,但这会“破坏”间距,也就是说我们需要手动干预才能获得正确的间距,但我想知道因为嵌套\(...\)在技术上应该是任意可能的)

得益于tcolorbox,上述内容可以很好地实现:

\documentclass[
    % answers,
]{exam}

\usepackage[
    raster,
    skins,
    % theorems,% `math` key
]{tcolorbox}

\usepackage{amsmath}

\usepackage{etoolbox}

\NewDocumentCommand{\answerblock}{mmmm}{%
    \tcbitem[
        raster multicolumn=3,% Full width
        title={Rearranged equation (symbolic)},
        equal height group=A,% All boxes in this group are forced to the same height
        % No matter the boxes natural height, conditionally leave some space for manual
        % answer entries:
        minimum for current equal height group=2cm,
    ]
        % Problem: the caller will pass the argument already set in math mode (set
        % inside `\( ... \)` -> DO NOT USE `$...$`). See also
        % https://tex.stackexchange.com/q/34830/120853 .
        % I asked about this here:
        % https://tex.stackexchange.com/q/593218/120853
        % We cannot put that argument *inside* another outer pair of `\(...\)`, it
        % will break.
        % This forces use to chain multiple math environments. This breaks the natural
        % spacing these have. Very sad and ugly, but we can hack a bit of space via
        % `\,`.
        % NOTE: DO NOT put text spaces here, they *will* show up in the result, since
        % we are in text mode in between the multiple math environments.
        #1\({}={}\)\ifprintanswers#2\fi%
    \tcbitem[
        raster multicolumn=2,
        title={Rearranged equation (numerical values)},
        equal height group=A,% All boxes in this group are forced to the same height
        % math,% from `theorems`; same issue with nested math ('Bad math environment delimiter')
    ]
        #1\({}={}\)\ifprintanswers#3\fi%
            \hfill{}\(=\)%
    \tcbitem[
        raster multicolumn=1,% 'Right' width
        title={Result},
        equal height group=A,% All boxes in this group are forced to the same height
        halign=center,
    ]
        \ifprintanswers#4\fi%
}

\NewDocumentEnvironment{solutionoranswerarea}{%
    d()% Force top box to this height, as a *percentage* of the *remaining* space
    O{% Top box descriptive text
        Collection of required equations%
    }
    +b% Environment body itself
}{%
    \begin{tcbitemize}[
        raster equal height=rows,% Core functionality to automatic sizing, see below
        raster every box/.style={
            valign=center,% Alignment for *content* inside of boxes
        },
    ]
    % The following is just an adaption from '15.6.2 Placing Spaces', see `tcolorbox`
    % manual page 312, version 4.42 (2020-10-09).
    % The idea is that we have two (the default) columns in an outer raster, each with
    % only a *single* row: 2 columns, 1 row.
    % However, the rows consist of `tcbitemize` environments, hence we can 'cheat' by
    % having two such inner environments in which we can put however many columns and
    % rows as we please.
    % The `raster equal height=rows` makes it so both *outer* rows are forced to the
    % same height through a couple iterations (=compilations); *within* the inner
    % `tcbitemize`, we can use a dynamic space macro to adjust a height to match said
    % outer height automatically (`space to` and `add to natural height`).
        \tcbitem[% First column (of default 2)
            blankest,% blankest style sets everything to empty/0pt
            space to=\dynamicfillspace,% (Forced height - natural height) -> save to macro
        ]
            \begin{tcbitemize}[% Nested itemization
                raster width=2\linewidth,% 2 columns, so double back to original value
                raster columns=3,% Full width
            ]
                \tcbitem[
                    raster multicolumn=3,% Full width
                    add to natural height=\dynamicfillspace,
                    title={#2},
                ]
                        #3
            \end{tcbitemize}
        \tcbitem[blankest]% Second column
            \begin{tcbitemize}[% Second nested itemization
                raster width=0pt,% 'Hide' this entirely
            ]
                \tcbitem[% Fixed height to which the first column will adapt (in height)
                    blankest,% blankest style sets everything to empty/0pt
                    height=\textheight-\pagetotal,% https://tex.stackexchange.com/a/207782/120853
                ]
            \end{tcbitemize}
}{
    \end{tcbitemize}
}
\begin{document}

\begin{questions}
    \question Please solve for \(d\). Write your answer below. Note the required fields.

        \begin{solutionoranswerarea}
            \begin{solution}
                \begin{align*}
                    a + b &= c \\
                    c &= 2d \\
                \end{align*}
            \end{solution}

            \answerblock{\(d\)}{\(\frac{a + b}{2}\)}{\(\frac{20 + 10}{2}\)}{15}
        \end{solutionoranswerarea}

    \clearpage
    \question Please solve for \(x\) \emph{and} \(y\).

        \begin{solutionoranswerarea}
            \begin{solution}
                \begin{align*}
                    z &= x \\
                    z &= y^{2} \\
                \end{align*}
            \end{solution}

            \answerblock{\(x\)}{z}{9}{9}
            \answerblock{\(y\)}{\(\sqrt{z}\)}{\(\sqrt{9}\)}{3}
        \end{solutionoranswerarea}

    \clearpage
    \question Please draw something.

        \begin{solutionoranswerarea}[Drawing]
            % Automatic solution requires extra work...
        \end{solutionoranswerarea}
\end{questions}
\end{document}

打印(lualatex在 TeXLive 2020 上;需要运行几次才能获得正确的自动间距):

在此处输入图片描述

在此处输入图片描述

在此处输入图片描述

使用answers课程选项,它打印(最后一页是相同的,该问题的解决方案不属于这个“M”WE 的一部分):

在此处输入图片描述

在此处输入图片描述

无法解决此问题的方法有:

  1. 使用tcolorbox定理库及其密钥。这会导致与嵌套时math相同的问题Bad math environment delimiter
  2. 我们\(\answerblock{}{}{}{}\)甚至无法做到这一点\(\tcbitem ...\),也就是在数学模式中包装整个宏。

上述问题仍然存在:

出于各种原因,调用者(即作者)应该控制何时进入数学模式。因此,我们得到了\(...\)的参数answerblock。像这样的文本输入\answerblock{...}{...}{\sqrt{9}}{...}是不可接受的(令人惊讶的、不透明的行为,并且混淆了语法突出显示/linting)。\(\answerblock{}{}{}{}\)可以但不起作用,所以我们也\TextOrMath遗憾的是不能使用

因此,需要手动干预间距,因为我们显然无法嵌套\(...\)。为什么会这样,有没有办法解决这个问题?\(...\)应该可以任意嵌套。

答案1

提议:

  • 将整个通话内容包含\answerblock在内\(\)
  • 不要将各个参数括在数学分隔符中
  • \answerblock在命令开始时关闭数学模式
  • \answerblock在命令末尾重新打开数学模式
  • \tcbitem在s内使用数学分隔符括住完整的数学表达式

这样,间距就可以保留,无需任何额外的语法,并且编辑器/linter 也可以被欺骗,认为数学是在数学模式下输入的。

MWE(注意 M):

\documentclass[answers]{exam}

\usepackage[raster]{tcolorbox}

% make syntax highlighters happy
\let\mathopen\(
\let\mathclose\)

\usepackage{xparse}

\NewDocumentCommand{\answerblock}{mmmm}{%
    \mathclose%
    \tcbitem[
        title={Rearranged equation (symbolic)},
    ]
        \(#1=\ifprintanswers#2\fi\)%
    \tcbitem[
        title={Rearranged equation (numerical values)},
    ]
        \(#1=\ifprintanswers#3\fi\)%
            \(\hfill{}=\)%
    \tcbitem[
        title={Result},
    ]
        \ifprintanswers#4\fi%
    \mathopen%
}
\begin{document}
\begin{tcbitemize}
% linter also happy
\(\answerblock{d}{\frac{a + b}{2}}{\frac{20 + 10}{2}}{15}\)
\end{tcbitemize}
\end{document}

结果:

在此处输入图片描述

答案2

Tex 命令几乎总是应该是数学或文本,而不是两者兼而有之,请注意 latex 具有用于文本和数学重音的\^符号\hat,尽管从技术上讲它们可以结合在一起。

然而,如果您确实想将它们合并为一个,LaTeX 可以满足\TextOrMath您的需求。

在此处输入图片描述

\documentclass{article}

\begin{document}

\newcommand*{\somecommand}[1]{\TextOrMath{#1 = \ignorespaces}{#1={}}}

an = in text with word spacing \somecommand{ABC} foo

an = in math with \verb|\mathrel| spacing  $\somecommand{ABC}x$

\end{document}

相关内容