一个 x² 难倒了三个终端:数学公式的显示、输入和判分

简介: 本文详解拾阶教育App在数学公式处理上的三大难题与解法:显示(三端差异化渲染)、输入(手机符号键盘设计)、判分(Unicode与LaTeX归一化比对),兼顾准确性、体验与工程可行性。(239字)

让孩子在手机上打出 x²

一道填空题,标准答案是 k^2。学生在手机上写了 k²,系统判 0 分。

学生没写错,系统也没有 bug,至少每一行代码单独看都没有。题库里的答案是 LaTeX 写法,学生手机键盘上能打出的是 Unicode 上标。比较之前,系统会先做一次 Unicode 规范化(NFKC),它把 k² 折成了 k2。于是 k^2 对 k2,不相等。

做学习类产品,数学公式是绕不开的。它在三个地方都会出问题:显示,学生看不看得懂题;输入,学生能不能把答案写出来;判分,写对了能不能被认出来。拾阶有网页、App、微信小程序三个终端,同一个公式在这三处各有各的坑。这篇把我们的解法摊开讲。

一、显示:同一个 LaTeX,三个终端三种画法

同一个 LaTeX,三个终端三种画法

我们的内容约定很简单:公式一律写 LaTeX($...$),排版一律 Markdown。约定只有一份,三个终端各自想办法把它画出来。

网页最省事,KaTeX 直接排。

App 是 React Native,没有现成的公式排版。摆在面前的路有两条:自己写一个 LaTeX 排版器,或者把 KaTeX 放进 WebView。

自己写排版器的问题不是做不出来,而是做不全。分式、根号、上下标好办;矩阵、化学方程式箭头上的条件、多行对齐不好办。做一半的排版器会让一部分题目显示成乱码,这比统一用 WebView 慢一点糟得多。所以 App 的讲解、题干、解析这类长文本走 WebView + KaTeX;列表里的短文本不值得为它开一个 WebView,转成近似的纯文本显示。KaTeX 加载失败也不白屏,公式原样显示成文本,简单的式子照样读得懂。

小程序最麻烦。小程序的富文本组件不认 SVG 标签,把 KaTeX 的输出放进去,根号和伸缩括号会消失;在小程序里跑 MathJax,又会撑爆 2MB 的主包。

最后的做法是让服务端画。网页那边加了一个接口,用 MathJax 把单个公式渲染成自包含的 SVG,长缓存;小程序把公式当图片插进富文本。图片在富文本里必须给出尺寸才显示,所以还有第二个接口:一页里的所有公式一次性批量询问宽、高和基线位置。MathJax 给的尺寸单位是 ex,换算成 em 再插进去,公式图片就和周围的文字一样大、基线对齐。

这里有个容易忽略的体验细节:尺寸回来之前显示什么?

最初显示的是 LaTeX 源码。网慢的时候,学生盯着题干上的「在 $-2$,$+3$,$0$ 中」看好几秒,像一串乱码。后来改成先显示纯文本近似(-2、x²、1/2),尺寸回来再无缝换成公式图片。

二、一个专门的坑:框起来的一整句中文

讲解里有一种写法很常见:

{
   mathJaxContainer[0]}

用公式框标出重点,排出来是一个带边框的结论。已发布的知识点里,九成都用到了它。

问题是 KaTeX 把它当成一整块公式,不会换行。网页上屏幕够宽,没事;App 里手机屏宽放不下,右半句直接被切掉;小程序里它是一张公式图片,缩到屏幕宽度以后字小得看不清。

这个问题单看代码是发现不了的:渲染没报错,数据也没问题,只有在真机屏幕上看才知道右半句没了。

解法是承认它的本质:这种框里装的是一句话,不是公式。所以当整条公式只有一个 \boxed、里面又有中文时,改写成一个能换行的文字框,中文照常排版,夹在中间的公式片段(比如 180^{\circ})仍按行内公式排。纯公式的框(\boxed{140^{\circ}})不动。

三、列表里的公式:只求认得出

错题本、成绩页的折叠卡片只显示两行题干。最早的做法是把公式一律换成「…」,结果数学题成了这样:

若关于 … 的方程 … 的两根均为正数,则 … 的取值范围是

一屏全是省略号,学生认不出是哪道题。每题都开一个公式渲染又太重。

所以写了一个转换函数,把公式转成能读的纯文本:x^2-3x+2=0 → x²-3x+2=0,\frac{1}{2} → 1/2,\angle → ∠。上下标只在每个字符都有对应的上标字形时才转,否则写成 ^(…);分子分母不止一个字的时候自动加括号。只求认得出,不求排版精确,点开以后仍是完整的公式渲染。

这个函数 App 和小程序是同一份,文件头写着「改一处要同步另一处」。

四、输入:一排符号键

显示解决了,接下来是学生怎么写答案。

手机键盘打不出 x²、√、π、≥。学生只好写「x的平方」「根号3」,在手机浏览器上还得切三四次键盘找符号。

我们的办法很朴素:在正在输入的框下面放一排符号键。

// 上下标单独一个字形太小看不清,键面画成 x²、x₁,插进去的只有上标本身
export const MATH_KEYS = [
  ...["²", "³", "^", "₁", "₂"].map((c) => ({
    label: c === "^" ? "xⁿ" : `x${
     c}`, insert: c })),
  ...["√", "π", "/", "(", ")", "°", "±", "×", "÷", "≥", "≤", "≠", "∠", "△", "⊥", "∥", "∞", "≈", "∈"]
    .map((c) => ({
    label: c, insert: c })),
];

做起来有几个细节,每一个都是踩过才知道的:

  • 插进去的是 Unicode 字形,不是 LaTeX。 学生看到的就是 x²+1,而不是 x^{2}+1。
  • 按下按键时不能让输入框失焦。 失焦了符号键就收起来,点击落空。所以按键在 pointerdown 时阻止默认行为。
  • 光标要放回符号后面。 受控输入框重新渲染后光标会跳到末尾,要等这一帧画完再把光标放回去。否则学生在中间插一个 √,光标就飞到最后了。
  • 只在需要的时候出现。 题干里有公式或数字才给符号键,语文、英语的填空用不上,平白占一行。

三个终端用的是同一套键位。同一道题,不能 App 上能打 x²、小程序上打不了。

五、判分:把不同写法折成同一种

学生的写法和题库的写法,折成同一种再比

回到开头那道题。学生写 k²,题库存 k^2。

以前全靠出题时把每种写法都列进答案,漏列一种,学生写对了也判错。题库里有上万道填空题,指望每道都列全是不现实的。

现在的做法是:两边都折成同一种线性写法再比。

x² / x^2 / x^{2}           → x^2
\sqrt{19} / √19 / √(19)    → √19
\dfrac{a^2}{4b^4} / a²/(4b⁴) → a^2/(4b^4)
180^\circ / 180°           → 180°

有一个顺序问题必须注意:上下标字形要在 Unicode 规范化之前处理。NFKC 会把 ² 折成普通的 2,等它折完再想转成 ^2 已经来不及了。开头那个 0 分就是这么来的。

另外两条原则:

  • 只会更宽,不会更严。 原来的比较照旧,新规则只在原来判错时再补一次。原来判对的,现在一定还判对。
  • 能按数值比的就按数值比。 0.5、1/2、50% 本来就按数值比;-\dfrac37 原本解析不成数字,折成 -3/7 以后也能参与数值比较了。

规则上线前,我们用本地题库里 1.4 万道填空题、1 万种不同的答案写法跑了一遍。新规则一共把 464 组写法合并成了相同,逐组看过,全部是同一个式子的不同写法,没有把两个不同的答案判成相同。

最后这一步最不能省。判分规则放宽是有风险的:放得太宽,错的答案也会被判对。「只会更宽」保证了不会冤枉学生,而那 464 组的人工核对保证了不会放过错误。

如果只带走几句

  1. 公式内容只存一种格式,显示交给每个终端各自解决,不要为某个终端改内容。
  2. 画不了就让服务端画。 小程序的限制很硬,绕不过去就换个地方做。
  3. 加载中的样子也是体验。 先显示纯文本近似,比显示源码好得多。
  4. 框里装的是什么,决定它该怎么排。 一句话就按一句话排,能换行。
  5. 判分放宽要有数据兜底。 改之前拿全量题库跑一遍,逐组看合并结果。

附:这些在产品里长什么样

网页版答题页,填空题下面那排符号键。图里的 -1/8 是用「/」键输入的。

答题页的符号键

拾阶网页版:填空题的数学符号键

小程序里的知识点讲解,公式是服务端画好的图片;「对顶角相等」那几个带边框的结论,就是改写后能换行的文字框。

小程序知识点讲解

拾阶小程序:知识点讲解里的公式与重点框

App 的错题本,折叠卡片里的题干是公式转出来的纯文本:x²+x+1=0、x²-√5x+1=0,一眼认得出是哪道题。

App 错题本

拾阶 App:错题本的题干预览

下一篇讲我们怎么发现这类「代码没错、页面不对」的问题:让 AI 当测试员,对着正式环境把 App 和小程序的每个页面点一遍。

相关文章
|
数据采集 前端开发 数据可视化
程序员必知:基于氚云平台的应用开发学习(一)
程序员必知:基于氚云平台的应用开发学习(一)
946 0
|
11月前
|
机器学习/深度学习 人工智能 自然语言处理
教育行业如何做GEO?让AI成为你的课程推荐官
过去,学生找课程靠搜索;现在,他们直接问AI:“附近有哪些性价比高的编程课?”或“商科最好的在线大学?”——AI不会简单罗列链接,而是直接推荐答案。如果你的教育机构没被AI“看见”,可能已经错过了新一轮流量红利。 作为深耕GEO领域的实战团队,数聚酷科技结合教育行业特性,总结出以下可落地的GEO优化策略,帮助你的课程和…
768 6
|
6月前
|
算法 网络安全 数据安全/隐私保护
如何逆向获取转转App的接口参数?
逆向获取转转 App 的接口参数(核心是 zzreqsign、zz-token、deviceId、timestamp 等加密 / 动态字段),是调用其私有 API 的关键。下面按 抓包 → 定位 → 算法还原 → 代码实现 四步,给你一套可落地的完整流程(2026 最新,Android 为主)。
|
监控 Linux 网络安全
使用 Kali Linux 实现 Smurf
使用 Kali Linux 实现 Smurf
|
数据库
要注销域名,您可以按照以下步骤
要注销域名,您可以按照以下步骤
7149 2
|
存储 缓存 自然语言处理
(三)JVM成神路之全面详解执行引擎子系统、JIT即时编译原理与分派实现
执行引擎子系统是JVM的重要组成部分之一,在JVM系列的开篇曾提到:JVM是一个架构在平台上的平台,虚拟机是一个相似于“物理机”的概念,与物理机一样,都具备代码执行的能力。
750 1
|
PyTorch 算法框架/工具 网络架构
UNet详细解读(二)pytorch从头开始搭建UNet
UNet详细解读(二)pytorch从头开始搭建UNet
800 0
|
网络协议 Ubuntu Linux
「远程开发」VSCode使用SSH远程linux服务器 - 公网远程连接
「远程开发」VSCode使用SSH远程linux服务器 - 公网远程连接
1365 0
|
11月前
|
存储 安全 网络协议
阿里云服务器经济型e、通用算力型u2i、计算型c9i、通用型g9i、内存型r9i实例详解与选购参考
在阿里云当前的活动中,可供用户挑选的云服务器实例规格丰富多样,主要包括经济型e、通用算力型u2i、计算型c9i、通用型g9i以及内存型r9i,常常感到无所适从。这些实例各具特色,性能各异,如何根据自身需求做出明智选择,成为众多用户关注的焦点。本文将详细解析这五大实例的性能特点、适用场景及选购建议,旨在帮助用户轻松挑选出最适合自己的云服务器实例,以供参考和选择。
|
人工智能 机器人
【超简单】让AI自动回复抖音评论
大家在刷抖音的时候,是不是发现有些视频底下的作者评论像是AI回复的,不是真人回答的。今天就来教大家,怎么用AI自动回复你在抖音发布视频中的评论。
5153 0