对于要关心语义信息的那些应用,内容型MathML标记会代替其它选择展现出强大的功能。本文结合实例描述了关于内容型MathML标记的各个方面,包括含义、特性、使用方法,以及与呈现型标记的混排等,在文章的最后又提出了一些关于实施Content MathML的建议。如果STM用户需要应对像计算机代数这样的语义相关的任务,例如在函数式语言LISP中使用内容型MathML作为一种表达方法,希望本文能成为这些工作的基础材料。
在技术世界里,好像从来也不曾只有一种核心力量,可以统制一切,不可替代。经常会同时存在多种竞争性的标准和方案,有时候,它们本身也由互为补充的几个子部分组成。处理Web数学文档的方式,归纳起来,有下面几种:1、图形页面显示;2、使用多种字符集的本地HTML;3、在HTML中嵌入图形;4、用Java applet生成公式;5、公式插件。在许多情形下,这些技术之间的关系,并不是简单的包含于或真包含于的关系,例如有时我们会把字符和嵌入图形同时混排。
OpenMath和MathML就是这样两种标准。OpenMath Esprit项目在开发时间上和W3C的MathML有着相当大的重叠,最初W3C并没有想主动开发出一种数学标记语言。1997年底在OpenMath协会开始工作不久,MathML 1.0就发布出来。MathML和OpenMath语言原先也是补充而不是竞争关系,MathML 2.0中加入了许多新的元素,这两门语言慢慢地融入了对方的一些因素。尤其是MathML,在内容元素上,它大量地参考了OpenMath项目和Stilo Technologies开发的一个DTD片段。但与OpenMath的单一标记不同,MathML却是由两种基本独立的语言组成。图一是MathML 2.0标准的基本组成结构:
一种是Presention Markup,另一种是Content Markup,它们用途不同。呈现型标记一般用于以两维结构可视化地描述数学标记,内容型标标记用来描述抽象的数学对象,提供表达式数学结构的下层编码,由于同时描述数学标记,两者有一定的对应关系。实际上,如果使用同一编码方法处理视觉和下层结构,就会发现缺少某些特定的呈现编码,因为它们被用来表示不同的语义信息。显示和语义之间的多对一和一对多的关系也使得数学公式的单一表达非常困难。再者,不同的文化,不同的时代对数学公式的表达也不相同,公式表达是与时代和文化息息相关的。清晰地编码下层数学结构,不考虑视觉或听觉表述,就可以更加准确地进行信息交换。
MathML内容型元素基本集的受众是接受K-12教育的学生,也就是说在美国教育中从幼儿园到高中结束的这段时期,可能也会适用于大学教育中的前两年。如图一所示,MathML内容型元素的主题领域基本上包括:
- 算术、代数、逻辑和关系
- 积分和矢量计算
- 集合论
- 序列和级数
- 基础经典函数
- 统计理论
- 线性代数
MathML从未宣称过建议元素集合对于这些领域是完整的,但它预留了可扩展的接口将会大大减少使用有限列表造成的问题。
由于本文作者
先前的一篇文章中 已经比较详细地介绍过呈现型MathML标记的用法,本文就主要讲述内容型MathML标记的使用方法。
在先前的文章中,我们是从Fermat小定理开始描述逐步深入描述MathML的。这里我们仍然从Fermat小定理开始,首先我们回顾一下它的数学定义:
n
p-1 ≡ 1 (mod p),如果n⊥p
它的MathML Content Encoding如下:
<math>
<apply>
<equivalent/>
<apply>
<power/>
<ci>n</ci>
<apply>
<minus/>
<ci>p</ci>
<cn>1</cn>
</apply>
</apply>
<apply>
<rem/>
<cn>1</cn>
<ci>p</ci>
</apply>
</apply>
</math>
基本上,内容型MathML编码基于表达树的概念,表达树的叶子节点是由一组预定义的原始对象,如容器、关系符、限定符和操作符等组成,表达树中的内部节点一般表示某种函数或按照一定规则组合而成的复合对象。我们从这个例子开始,逐步地讲解内容编码中重要的基本原始对象。
因为MathML是XML的一种应用,所以用<math>…</math>括起的代码段会是一段格式良好的代码。在这段代码中,第一个碰到的内容标记是apply,它可能是内容型MathML元素中用得最多的一个标记了,它的作用是把函数或操作符作为参数使用。它经常用以编组,这其实意味着公式片断由可见或不可见的"逻辑"括号围绕。apply的用法如下:
<apply> op a b [c…] </apply>
op表示操作符或函数,a、b等都是操作数,它们本身也可以是容器或其它内容型元素。由于apply本身也是容器类元素,所以经常嵌套使用,如上面就多处使用了apply标记。在MathML内容型标记还有一个reln与apply具有相似作用,但它表示的操作数之间大于、小于等数学关系,而不是操作符或函数。MathML 2.0中,apply也可使用逻辑操作符,保留reln仅仅是为了兼容的目的。例如,表达式x > y使用reln表达就是:
<reln>
<gt/>
<ci>x</ci>
<ci>y</ci>
</reln>
上面的gt和equivalent(≡)一样,都用来表示数学中的逻辑关系符。MathML中的内容型关系元素还有eq、neq、lt、geq、leq、approx(a≈b)、factorof(a?b)。
数字和字符分别由元素cn和ci表示,对应于呈现元素mn和mi,用法和mn与mi基本相同。cn和ci中有一些常用的属性,也有必要介绍一下。cn的属性base表示基数,例如base="8"表示按照八进制表示数据,缺省基数值是10。cn和ci中共有的属性type表示数据类型,ci中缺省值是"",常设值是"vector"。cn的type属性可以设置的值包括"e-notation"、"integer"、"rational"、"real"、 "complex-polar"、"complex-cartesian"和"constant"。例如复数2+3i的表示就是:
<cn type="complex-cartesian">2<sep/>3</cn>
sep用来把cn的PCDATA划分成几个独立的标记,在构造有理数和复数时经常会用到sep。如果它出现在MathML元素之间,就会出现错误。csymbol是另一个Token Element,它允许创建在MathML外部定义的元素,这个元素随后就可以当作操作符或常量来使用。
power、minus和rem同属内容型标记中算术代数逻辑一支,常见的算术、代数和逻辑运算符都可以在这一支中找到。它们的用法全部都是在空标记之后紧跟操作数,这点不用多谈。这类标记还包括:Quotient (quotient)、Factorial (factorial)、Division (divide)、Maximum and minimum (max, min)、Universal quantifier (forall)、Existential quantifier (exists),等等。
实际上,上面所有这些代码都是使用Web EQ Editor从Presentation Encodings转换得到的。使用呈现类元素的约束是要少许多的,因此这样的转换过程可能会带来一定的风险,致使语义不完整或出错错误。例如,上面的1 (mod p)如果直接转换,就会产生错误,正确的写法应该是1 mod p。
上面一节中,实际上已经讲了标记符、算术、代数和逻辑关系元素。在MathML中,还有其它几类内容元素,首先要讲一下其它一些基本的内容元素。
除apply、reln之外,常用的基本内容元素还有fn,fn是function的缩写形式,它用来创建如预定义函数sin或cos一样的对象。fn必须有一个子元素来给出函数名。当fn是apply的第一个子元素时,后面的参数数目是由fn的内容决定的。在MathML 2.0中,fn元素已经过时,扩展已有的数学函数,现在一般用csymbol元素或用declare配合lambda一起表示。
interval元素用来表示数学区间,它的属性closure可以采用的值包括"open"、"closed"、"open-closed"或"closed-open"。例如,区间[-1,1]的表示就是:
<interval>
<cn>-1</cn>
<cn>1</cn>
</interval>
上面提到的declare的作用有两种,一是改变或者设置数学对象的属性值,二是建立名称和对象之间的联系。声明本身不会显示出来,但是会间接影响到之后所有声明对象使用当中的语义。声明必须在math元素头部进行,声明作用域是整个math元素范围。声明包括一个或两个孩子,第一个是强制性的,表明声明的对象,第二个是可选的,是初始化变量的构造器(constructor)。例如,下面的声明:
<declare type="vector">
<ci>A</ci>
<vector>
<ci>a</ci>
<ci>b</ci>
<ci>c</ci>
</vector>
</declare>
这段代码中的A被声明为矢量(a, b, c),后续的V = A + B之类的编码虽然保持不变,但表达式却会按照矢量加法正确翻译。构造器类型与declare属性type指定的类型必须一致,如上面两者类型就同为vector。如果没有设置类型属性,那么声明对象的类型就自动设置成构造器的类型。
condition元素用来断言布尔表达式,常常用来表示数目不能明确指定的集合或列表。举例说明,下面的代片段就表示{x, x > 1}。
<set>
<bvar>
<ci> x </ci>
</bvar>
<condition>
<reln>
<gt/>
<ci>x</ci>
<cn>1</cn>
</reln>
</condition>
</set>
代码段中的bvar元素是把"绑定变量"的值以某种方式限制到某个特殊的应用领域中。bvar的涵义依赖于所处环境的上下文。例如,在积分中它确定积分变量,在导数中它指定被微分函数变量(这时需要带上子元素degree以确定导数的阶)。上面的bvar按照某种条件规则定义集合,而不是进行缺省的遍历定义。bvar与lowlimit、uplimit一样都是限定符元素,它们配合一些操作符,如积分、级数等,也可以使用用户自定义的函数。限定符的使用与apply元素近似,一般地,它们必须按照一定顺序出现,其含义与所用操作符相关。
内容元素中还有一类语义映射元素,包括annotation、semantics和annotation-xml。annotation元素是一个容器元素,用于对非XML格式内容进行语义注解,它总是与semantics一起出现。annotation元素的encoding属性经常取的值有:TeX、Maple、Mathematica。与annotation不同,基于XML的表示需要括在annotation-xml对中,它的encoding属性经常的取值有:MathML-Presentation、OpenMath。semantics元素也是容器元素,它把给定MathML表达与另一些表示联系起来。它的第一个孩子是待注解的表达式,第二个孩子是注解。在屏幕上绘制时,会缺省绘制第一个孩子的内容。例如,下面有关semantics和annotation的代码:
<semantics>
<apply>
<plus/>
<apply>
<sin/>
<ci>x</ci>
</apply>
<cn>1</cn>
</apply>
<annotation encoding="TeX">sin x + 1
</annotation>
</semantics>
与呈现型元素相同,内容元素中也有一些常量符号,它们是使用XML空元素表示的。例如: <pi/>、<eulergamma/>、<infinity/>、<true/>、<false/> 等,分别表示π、γ、∞、true、false。
剩余其它元素大多都是面向某一具体领域的,例如,积分、统计、线代、级数等,或是一些基础函数,使用方法非常简单,与前面所讲元素相似,如需详细了解,请参阅W3C的MathML推荐标准第四章。
呈现元素用来捕捉数学对象的标记结构,以便利在不同的媒体上的绘制,它并不直接关心表达式的数学结构或含义。在许多情形下,标记结构与数学结构是紧密相关的,一些复杂的软件需要从标记结构中推导出语义信息。内容元素不关心如何显示的问题,如何准确地表达语义信息是它所关心的内容。这两种标记都是必需的,它们合在一起才能完整地表达公式信息。但同一种数学思想可以表达成好几种方式,同一种标记又可以表示好几种思想。语义和呈现元素的混排可以同时传送外观和语义信息,这要比从一个当中推导出另外一个来得更有效,同时也消弥了上面陈述的这种歧义现象。采用何种标记或标记混排,完全取决于作者本身的需求,如何进行控制,如何进行复用。
在MathML中标记混排有两种方式,第一种方式是两种元素散布在一棵单株的树中,称为混合标记(mixed markup);第二种是使用一对树同时提供两种表达,称为并行标记(parallel markup),首先看混合标记法。
如果作者的主要目的是显示表达式,增加一些内容标记会使产生更易于理解和复用的结果;如果作者的主要目的是控制内容,增加呈现标记会提供修改或定义内容如何显示的方法,另外一些新兴领域并无相关内容标记定义,从而需要自定义标记,如何显示自定义标记,就需要增加呈现描述来完成。例如,W3C文档中提供的下述代码:
<apply>
<eq/>
<apply>
<semantics>
<mi>rank</mi>
<annotation-xml encoding="OpenMath">
<OMS name="rank" cd="linalg4" xmlns="http://www.openmath.org/OpenMath"/>
</annotation-xml>
</semantics>
<apply>
<times/>
<apply> <transpose/> <ci>u</ci> </apply>
<ci>v</ci>
</apply>
</apply>
<cn>1</cn>
</apply>
它表达了公式rank(u
Tv)=1,其中,符号rank的语义来源于OpenMath的内容词典(Content Dictionary,CD)。
混合两种标记的最重要的原则是要保证混合之后结果仍有意义,要避免二义性和有问题的表达,随意组合是被禁止的。一般地,在内容标记中包含呈现标记,只有三种方式:1、ci和cn元素内;2、csymbol元素内;3、在semantics元素内。在呈现标记中包含内容标记的原则是表示结果无二义。一些内容元素与周围环境的语义相关,如bvar,如degree,这类元素是不允许在呈现模块中使用的。又如元素declare、sep、annotation和annotation-xml仅出现在特定上下文中,因此不能作为孩子用在呈现元素中。
另一种并行混排方法是同时提供内容和呈现两种记法给那些需要的应用,这种方法是通过内容元素semantics来完成的。例如,并行表示sinx的代码如下:
<semantics>
<mrow>
<mrow>
<mi>sin</mi>
<mrow>
<mo>(</mo>
<mi>x</mi>
<mo>)</mo>
</mrow>
</mrow>
</mrow>
<annotation-xml encoding="MathML-Content">
<apply>
<sin/>
<ci>x</ci>
</apply>
</annotation-xml>
</semantics>
原则上,可以嵌套使用semantics,表示任何节点和子元素。为了更好地适应应用程序,MathML提供了使用属性id和xref配合semantics元素确定对应子元素,进行交叉引用的方法,在这里就不多说了。更详细的内容,读者可以参阅W3C标准中相关部分。
为了防止误用MathML内容元素,要尽可能规范地编写MathML文档。要努力做到这一点,需要遵循一些已有的关于最佳实践的建议,其中也包括我自己的一些意见。
- 第一,也是最重要的一条,就是查阅W3C的MathML推荐标准,遵循标准当中例子的表述方法。
- 任何Content MathML中的内容,周围都要使用标签。不要在<ci>中嵌入多个字符,就算是逗号,也要单独表示。
- 不要使用fn和reln这样的过时元素,它们仅是为了兼容而存在的,要用属性type="fn"的ci元素。
- 不要在单个内容项目周围使用apply。
- 表示实体时,要使用实体名而不是Unicode值。
- Content MathML中的实体空标记<pi/>、<infinity/>、<integers>等,要使用这些空标记,而不要用实体名,如<ci>π</ci>。
- 有一些内嵌操作符在Content MathML中并不支持,如远大于符">>",就要这样使用:
<apply>
<mo>≫</mo>
<ci>x</ci>
<cn>0</cn>
</apply> - 当使用下标时,要这样写,注意所有内容都要包在ci里面。
<ci>
<msub>
<mi>X</mi>
<mn>0</mn>
</msub>
</ci> - 表示拉普拉斯变换,要记得它是操作符,不要使用<ci type="fn">。
<apply>
<mo>ℒ</mo>
<ci>x</ci>
<ci>t</ci>
</apply> - 用<root>表示平方,不要使用<degree><cn>2</cn></degree>。
- 当在偏差中针对某元素使用degree时,需要在bvar元素内容来指定,在bvar外部指定的degree是要对整个偏差起作用的,例如:
< apply>
<partialdiff/>
< bvar>
<degree><cn>2</cn></degree>
<ci>x</ci>
</bvar>
<bvar><ci>y</ci></bvar>
<bvar><ci>z</ci></bvar>
<degree><cn>4<cn></bvar>
<ci>f</ci>
</apply> - 表示负数的时候,要采用<cn>-1</cn>,而不要使用minus。
- 不要过度使用空格。
当然了,在使用MathML的过程中,我们还会总结出其它一些最佳实践出来,但这些仍是需要注意的。
尽管Content MathML并不是处理语义的唯一选择,但它们确实能够帮助解决一些问题,特别是使跨语言、平台、系统的那些集成应用变得更加容易。在本文中,您已经了解到了关于Content MathML的基本内容,也了解到如何实施最佳实践,以更好地编写Content MathML文档。如果要在Web平台上实施应用的话,Content MathML应该会是首选。