代码之家  ›  专栏  ›  技术社区  ›  paugier

Math Latex宏用于在reStructuredText和Sphinx中进行替换

  •  6
  • paugier  · 技术社区  · 10 年前

    使用 Sphinx reStructuredText ,是否可以定义数学宏以在数学Latex公式中进行替换?

    基本上,我想要一些

    .. |bnabla| replace:: :math:`\boldsymbol{\nabla}`
    .. |vv| replace:: :math:`\textbf{v}`
    
    .. math:: \rho \mbox{D}_t |vv| = - |bnabla| p + \rho \textbf{g} + \eta |bnabla|^2 |vv|,
    
    where |vv| is the velocity and |bnabla| is the nabla operator.
    
    Then follow many other equations with |vv| and |bnabla|...
    

    但它根本不起作用。首先,在数学模式中,标志不被替换;其次,即使它们被替换 :math: 语句在 .. math:: 块改变狮身人面像中的这两种行为,是不是一个好主意,而不是使其复杂化?

    另一个解决方案是像本问题中那样使用乳胶宏 Creating LaTeX math macros within Sphinx 但我认为,使用本地rst替换,最终代码将更容易阅读,就像我的示例中那样。我希望有清晰的公式,也可以在文本模式下阅读。

    此外,我使用的是MathJax扩展,因此无法使用变量pngmath_latex_preamble。我可以将此解决方案用于MathJax https://stackoverflow.com/a/19268549/1779806 但这似乎相当复杂,而且通过“局部”rst替换,代码将再次更清晰。

    编辑:

    我意识到,对许多人来说,实现一个 mathmacro 直接在reStructuredText中(即在Python包docutils中)执行替换指令。新指令的工作原理如下:

    定义:

    .. |bnabla| mathmacro:: \boldsymbol{\nabla}
    .. |vv| mathmacro:: \textbf{v}
    

    这些数学宏可以直接包含在文本中,如

    |bnabla| is the nabla operator,
    

    这应该会产生一个这样的内联方程式

    :math:`\boldsymbol{\nabla}` is the nabla operator.
    

    这些宏也可以包含在内联公式中:

    This is an inline equation :math:`\vv = \bnabla f`, 
    

    这应该相当于

    :math:`\textbf{v} = \boldsymbol{\nabla}f`
    

    它们也可以包含在块方程中

    .. math:: \vv = \bnabla f
    

    这应该相当于

    .. math:: \textbf{v} = \boldsymbol{\nabla}f.
    

    然而,我真的不熟悉docutils的内部工作方式。我特别注意到MathBlock指令(在docutils/parsers/rst/directives/body.py中定义)不调用对其输入的任何解析,因此在数学模式中没有替换。

    我不知道是否有可能改变替换指令的行为,以使替换更清晰,并适应其在文本、内联数学或块数学中调用的上下文。

    有人能给我一些关于如何实现这个有用的新功能的线索吗?

    1 回复  |  直到 4 年前
        1
  •  8
  •   paugier    9 年前

    因为我真的需要一个很好的解决方案来解决这个问题,所以我自己解决了。。。它给我带来了一段时间的工具,解决方案可能并不完美,但至少它工作得很好。我给出了结果,因为它对其他人有用。

    我还将该解决方案改编为Sphinx扩展 here .

    我必须定义一个新的替代指令,并重新定义数学指令和角色。所有这些都在文件mathmacro.py中完成:

    """
    'Proof of concept' for a new reStructuredText directive *mathmacro*.
    
    Use for example with::
    
      python mathmacro.py example.rst example.html
    
    """
    
    import re
    
    from docutils.parsers.rst.directives.misc import Replace
    
    from docutils.parsers.rst.directives.body import MathBlock
    from docutils.parsers.rst.roles import math_role
    
    def multiple_replacer(replace_dict):
        """Return a function replacing doing multiple replacements.
    
        The produced function replace `replace_dict.keys()` by
        `replace_dict.values`, respectively.
    
        """
        def replacement_function(match):
            s = match.group(0)
            end = s[-1]
            if re.match(r'[\W_]', end):
                return replace_dict[s[:-1]]+end
            else:
                return replace_dict[s]
    
        pattern = re.compile("|".join([re.escape(k)+r'[\W_\Z]|'+re.escape(k)+r'\Z'
                                       for k in replace_dict.keys()]), 
                             re.M)
        return lambda string: pattern.sub(replacement_function, string)
    
    
    class MathMacro(Replace):
        """Directive defining a math macro."""
        def run(self):
            if not hasattr(self.state.document, 'math_macros'):
                self.state.document.math_macros = {}
    
            latex_key = '\\'+self.state.parent.rawsource.split('|')[1]
            self.state.document.math_macros[latex_key] = ''.join(self.content)
    
            self.state.document.math_macros_replace = \
                multiple_replacer(self.state.document.math_macros)
    
            self.content[0] = ':math:`'+self.content[0]
            self.content[-1] = self.content[-1]+'`'
    
            return super(MathMacro, self).run()
    
    
    class NewMathBlock(MathBlock):
        """New math block directive parsing the latex code."""
        def run(self):
            try:
                multiple_replace = self.state.document.math_macros_replace
            except AttributeError:
                pass
            else:
                if self.state.document.math_macros:
                    for i, c in enumerate(self.content):
                        self.content[i] = multiple_replace(c)
            return super(NewMathBlock, self).run()
    
    
    def new_math_role(role, rawtext, text, lineno, inliner, 
                      options={}, content=[]):
        """New math role parsing the latex code."""
        try:
            multiple_replace = inliner.document.math_macros_replace
        except AttributeError:
            pass
        else:
            if inliner.document.math_macros:
                rawtext = multiple_replace(rawtext)
    
        return math_role(role, rawtext, text, lineno, inliner,
                         options=options, content=content)
    
    
    if __name__ == '__main__':
    
        from docutils.parsers.rst.directives import register_directive
        from docutils.parsers.rst.roles import register_canonical_role
    
        register_directive('mathmacro', MathMacro)
        register_directive('math', NewMathBlock)
        register_canonical_role('math', new_math_role)
    
    
    
        from docutils.core import publish_cmdline, default_description
        description = ('Generates (X)HTML documents '
                       'from standalone reStructuredText sources. '
                       +default_description)
        publish_cmdline(writer_name='html', description=description)
    

    文件example.rst的内容:

    Here, I show how to use a new mathmacro substitution directive in
    reStructuredText. I think even this small example demonstrates that it
    is useful.
    
    
    First some math without math macros.  Let's take the example of the
    incompressible Navier-Stokes equation:
    
    .. math:: \mbox{D}_t \textbf{v} = 
       -\boldsymbol{\nabla} p + \nu \boldsymbol{\nabla} ^2 \textbf{v}.
    
    where :math:`\mbox{D}_t` is the convective derivative,
    :math:`\textbf{v}` the velocity, :math:`\boldsymbol{\nabla}` the
    nabla operator, :math:`\nu` the viscosity and
    :math:`\boldsymbol{\nabla}^2` the Laplacian operator.
    
    
    .. |Dt| mathmacro:: \mbox{D}_t
    .. |bnabla| mathmacro:: \boldsymbol{\nabla}
    .. |vv| mathmacro:: \textbf{v}
    
    Now, let's use some math macros and try to get the same result...  The
    Navier-Stokes equation can now be written like this:
    
    .. math:: \Dt \vv = - \bnabla p + \nu \bnabla^2 \vv
    
    where |Dt| is the convective derivative, |vv| the velocity, |bnabla|
    the nabla operator, :math:`\nu` the viscosity and :math:`\bnabla^2`
    the Laplacian operator.