ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

我没用 mathjs,手搓了一个会算微积分、还敢报误差的函数图像计算器

我没用 mathjs,手搓了一个会算微积分、还敢报误差的函数图像计算器 我没用 mathjs手搓了一个会算微积分、还敢报误差的函数图像计算器一键开通华为云码道 CodeArts 代码智能体 https://developer.huaweicloud.com/codeartsco.html?sourcedmzntgwatomgit1sourceaddmzntgwatomgithd作品介绍一个纯前端、零第三方依赖的函数图像计算器。输入一个表达式比如sin(x)x/4它当场把曲线画出来还顺手用数值方法算出零点、极大极小值、指定区间的定积分、某点的导数全摆在右侧一张数值体检卡上。听起来就是个 mini Desmos 对吧但我不想停在画得像。这个项目真正想证明的是另一件事这些数值结果不是蒙的它们能跟解析真值对拍。我算的 ∫₀^π sin x dx跟教科书里的精确值 2 差 6.5e-14我算的 x²-2 的根跟 ±√2 差 6e-11我算的 d/dx(x³) 在 x2跟解析的 12 差 2e-10。71 项单元测试全绿一份npm run verify的对拍报告全是 PASS。仓库公开你可以 clone 下来自己跑。为什么非要从零写平时我要画个函数Desmos、GeoGebra 随手就用。但有次我想验证一个数值积分的实现发现这些工具要么不给中间数值、要么你根本不知道它怎么算的。我就冒出一个念头表达式解析、求根、求导、积分这套东西说难不难我能不能一行一行自己写出来而且写完还能自证算得对。这正好也是这次码道比赛我想试的路子——让 AI 编程智能体去啃一个有客观真值可对拍的活。画得好不好看是主观的但∫₀^π sin x 到底等不等于 2是客观的是或否赖不掉。可验证是我给它选的护城河。先跑起来看别听我吹先看几个真实函数长啥样、算得准不准。默认打开是sin(x)x/4一条平滑的、带点斜抬的正弦阻尼振荡exp(-x/4)*cos(3x)衰减的正弦三次多项式x^3-3x两个极值、三个零点全被数值法标出来然后是我最看重的对拍。跑npm run verify数值 vs 解析真值逐条比下面讲它是怎么造出来的以及中间踩的坑。表达式解析器优先级和结合性的坑第一轮我只让码道干一件事从零写一个数学表达式解析器不许用 eval、不许用 Function、不许 import mathjs。就是词法切 token → 语法建 AST → 求值。给 AI 派活我学乖了提示词必须钉到函数级、还要塞进锚定外部真值的测试要求否则它会给你一个看着能跑的糊弄版。我第一轮的原话大概这样src/tokenizer.js —— 词法数字/标识符/运算符 -*/^%/括号/逗号支持小数和科学计数法。 src/parser.js —— 递归下降/Pratt 解析正确处理优先级与结合性 ^ 右结合且高于一元负号* / % 同级 - 同级。 src/eval.js —— 求值支持变量 x、常量 pi/e、函数 sin cos tan exp ln sqrt abs pow ... 测试锚定外部已知真值不能自证。 直接建文件并 git commit push不要只描述、不要贴代码正文。最后那句直接建文件并提交是血泪教训——不加码道有概率给你写一大段我打算这样做的方案一个代码不落。优先级这块测试卡了几个经典值全过parseEval(23*4)14// * 高于 parseEval(2^3^2)512// ^ 右结合2^(3^2)parseEval(-2^2)-4// 一元负号低于 ^-(2^2)parseEval(2^-3)0.125// ^ 后可跟一元负号-2^2到底是 -4 还是 42^3^2是 512 还是 64这些就是解析器的照妖镜。码道用 Pratt 解析器把绑定优先级写对了核心就是一张优先级表加右结合时递归到同级、左结合时升到下一级的处理// 内置函数名用于区分函数调用与隐式乘法constKNOWN_FUNCSnewSet([sin,cos,tan,asin,acos,atan,exp,ln,log,sqrt,abs,floor,ceil,pow,min,max]);// 优先级值越大越先算^ 最高且右结合* / % 次之 - 最低constPREC{^:80,*:70,/:70,%:70,:60,-:60};词法层还要处理科学计数法1e-3这种看着像两个 token 其实是一个数字的情况不然2e-3会被切成2、e、-、3直接崩。隐式乘法一个便利功能引爆的连锁反应第一轮做完我以为齐活了。直到三轮专家评审后面讲里技术专家指出来用户输入2x、2sin(x)、2(x1)会直接抛unexpected token。对一个函数图像计算器来说这体验一下就露怯了——谁写函数不写2x啊。于是我在 R6 让码道补隐式乘法。它的做法挺漂亮在词法之后、语法之前插一道// 隐式乘法在值结尾token 与值开头token 之间插入 *functioninsertImplicitMul(tokens){// 值结尾: num, id, ) 值开头: num, id, (// 例外: id 后跟 ( 且该 id 是已知函数 → 函数调用不插入 *}那个例外是关键。2x要变成2*x但sin(x)绝不能变成sin*(x)。所以遇到标识符后跟左括号时得先判断这个标识符是不是已知函数是就当函数调用、不插乘号。结果这个改动引爆了一个连锁 bug码道发现原来的未知函数测试挂了——foo(1)现在被当成foo * (1)报的错从unknown function foo变成了variable foo is not defined。功能没错但错误语义变了。它自己定位到、改了测试、重跑。这种加个便利功能把别处搞坏、再被测试逮住的来回特别真实。补完隐式乘法后2x1这种写法就能直接出图了数值分析三件套第二轮做数值层。求根、求导、积分都是教科书算法码道写得很干净。中心差分求导exportfunctionderivative(f,x,h1e-5){return(f(xh)-f(x-h))/(2*h);}复合 Simpson 积分注意它先保了 n 是偶数exportfunctionintegrate(f,a,b,n1000){if(n%2!0)n;// Simpson 要求偶数分段consth(b-a)/n;letsumf(a)f(b);for(leti1;in;i){constxai*h;sumf(x)*(i%20?2:4);}return(sum*h)/3;}求根用均匀采样找变号区间 二分收敛exportfunctionfindRoots(f,a,b,{tol1e-9,samples2000}{}){// 采样prevY*curY0 说明区间内有根二分逼近到 tol}这里n % 2 ! 0那行不起眼但很要命——Simpson 法则的 n 必须是偶数否则最后一段系数错、精度直接从 1e-14 退化。这种细节正是手写和调库的区别。那个把 tan 极点当根的 bug技术专家评审时拿tan(x)在 [0,4] 一试发现 findRoots 返回了{0, π, π/2}。π/2 哪来的根tan 在 π/2 是竖直渐近线函数根本没定义值从 ∞ 跳到 -∞采样时prevY*curY0判定为变号二分就傻乎乎收敛过去了。这是个典型的变号≠有根陷阱。修法是在二分收敛后加一道校验只保留|f(root)| tol且 f 在该点有限的根把渐近线跳变造成的伪根剔掉。修完tan 的根就只剩真正的 0 和 π 了。而 tan 的图恰好也展示了绘图层处理间断点的正确姿势——曲线在 ±π/2 处自动断开成独立分支不会画出一条穿过渐近线的假竖线可验证护城河数值 vs 解析对拍这是整个项目的魂。verify/numeric-vs-analytic.mjs对一批有解析真值的用例跑数值结果逐条比误差[integral] maxAbsErr6.53e-14 PASS ∫₀^π sin x: numeric2.00000000e0 analytic2.00000000e0 err6.528e-14 [roots] maxAbsErr2.98e-10 PASS x²-2 正根: numeric1.41421356e0 analytic1.41421356e0 err6.335e-11 [derivative] maxAbsErr2.12e-10 PASS d/dx x³ x2: numeric12.00000000 analytic12 err2.118e-10 [extrema] maxAbsErr4.77e-10 PASS sin x 极大 x: numeric1.57079633 analytic1.57079633 err3.745e-10∫₀^π sin x 数值算出来跟精确的 2 差 6.5e-14x²-2 的根跟 √2 差 6e-11。这些真值2、√2、12、π/2不是我实现里跑出来的是外部数学事实测试拿它们当尺子量我的数值方法——这才叫对拍而不是自己给自己发奖状。SVG 画图 白色系单页第三轮做渲染和页面。曲线用 SVG path坐标轴、网格、极值零点标注全画进去。这里 UI 专家评审揪了个最致命的只画了网格线没标刻度数字——一个绘图器读不出坐标值专业度直接崩。R6 补了 nice-step1/2/5×10^k的轴刻度。还有积分阴影app.js 一开始把面积多边形的上下界传成了同一个值y0y1高度 0勾了积分区间啥也看不见。修成从曲线到 x 轴填充才出来。页面走白色系浅底 #f5f8fc、白卡、主色蓝 #3d7fff、深字 #17233b克制不堆料。换个函数、勾个选项曲线和数值实时刷新。勾上积分区间、填 0 到 π曲线下方到 x 轴之间就铺一层淡蓝阴影体检卡同步报出这段的数值积分数值体检卡是这项目的脸面它把数值法算的、可与解析解对拍这件事直接摊在页面上// 体检卡每行数值结果 一句全局说明「数值法·可与解析解对拍」// 零点、极大极小、∫ₐᵇ f(x)dxSimpson、f(x₀)中心差分实时刷新这里还有个我特意让码道补的细节表达式里可以直接写pi当积分上限比如 ∫₀^pi sin x这恰好又秀了一把解析器对常量的支持——普通计算器输入框只能填3.14159我这儿能填pi。三轮专家评审揪出的硬伤写完我以为挺完整拉了 UI、技术、产品三个专家评审各自读代码和截图打分技术 78、UI 72、产品 84。分不高但问题实在。技术专家那三条我全认隐式乘法没做、tan 极点当根、边界极值漏报x² 在 [0,2] 的最小值在端点 0导数不变号所以报不出。UI 专家揪了轴刻度和积分阴影。产品专家给了句我特别喜欢的话——它不只会画图每个数值结果旁边都写着解析真值和 1e-10 的误差。这几条我合成 R6 一轮全修了补隐式乘法、加极点校验、纳入端点极值、画轴刻度、修积分阴影、把对拍结果做成体检卡的一等公民。测试从 61 涨到 71全绿。码道 push 反复横跳才是真·开发日常技术之外的折腾也得如实记一笔。谎报 pushR2 那次码道回复里斩钉截铁说仓库已 push 到 public我 clone 下来一看——远端还停在 R1numeric.js 压根没上去。它把我执行了 push 命令当成了push 成功了。从那以后我在提示词里加了硬要求push 完必须再跑git ls-remote origin main把远端 40 位 SHA 打出来我要看到它等于刚提交的 commit。remote is aheadR5、R6 提交后 push 被拒远端比本地新码道得 pull --rebase、解决 evidence 报告的冲突、把 R6 版本放顶上、rebase --continue一顿操作才推上去。流量高峰 / 配额好几轮跑到一半甩检测到流量高峰正在为您重试或当前模型配额不足 [APIError]“。会话上下文涨到 100% 时它会只讲不做”只能开新对话重新绑仓库——好在 CodeArts 会自动压缩上下文压回 20% 又能接着干。最坑的一次老会话沙箱被码道自己写的假 token 污染了 git 凭据怎么推都认证失败最后是开了个全新会话干净沙箱才把 R2 重推成功。公开仓库仓库公开R1 到 R6 六次提交从表达式解析器一路到专家评审修复提交链清清楚楚。README 里功能亮点、可验证护城河、复现命令、架构分层都写了。语言构成 JavaScript 为主。提效数据环节轮次测试数关键产出表达式引擎R135词法Pratt 语法求值优先级/结合性锚定真值数值分析R247求根/导数/积分/极值 对拍解析真值取证渲染页面R355SVG 绘图、白色系 SPA、零依赖服务器体检卡对拍报告R461数值体检一等公民、numeric-vs-analytic 报告、响应式READMER561完整中文 README专家评审修复R671隐式乘法、极点伪根、边界极值、轴刻度、积分阴影全程我一行代码没手写除了本地跑验证的脚本但每一轮都 clone 下来自己node --test复核不是码道说绿就绿。verify 报告也是我本地独立跑出来的。五维自检交作业前我自己过了一遍五个维度也顺便给同样在用码道做可验证型项目的人一个参照原创度表达式解析器 数值分析全从零手写不碰 mathjs/nerdamer/desmos 的任何一行护城河是对拍不是套壳。可验证性71 项单元测试锚定外部解析真值npm run verify把数值 vs 解析的误差逐条打出来任何人 clone 都能复现不靠我一张嘴。完成度四种函数形态、可调范围/采样/配色、积分阴影、零点极值标注、PNG/SVG 下载、白色系响应式页面能当工具真用。踩坑深度隐式乘法引爆未知函数检测、tan 极点当根、Simpson 的 n 必须偶数、轴刻度缺失、push 谎报成功——都是真撞上的不是编的。诚实度专家评审给的分不高72~84硬伤我全列了出来也全修了没修干净的地方比如某些函数没有解析真值可对体检卡那行只能显示—我也没藏。本地怎么跑gitclone https://atomgit.com/gcw_QMYlF6Ie/fn-plot.gitcdfn-plotnpmtest# 71 项单元测试npmrun verify# 数值 vs 解析真值对拍全 PASSnpmrun serve# 打开 http://127.0.0.1:8099/ 看白色系页面写在最后这个项目最想证明的还是那句在 AI 什么都能生成的年代“能生成早就不值钱了值钱的是能自证”。我让码道从零写表达式解析器和数值方法然后逼它拿 ∫₀^π sin x2、x²-2 根±√2 这些外部解析真值去对答案误差打到 1e-14。它中途因为一个隐式乘法把未知函数检测搞坏、被自己的测试逮住也被三个专家评审按在地上摩擦了一轮才收敛到能看的样子。trust but verify。画图谁都会敢把误差报在桌面上才是这个 mini 计算器跟 Desmos 拉开差距的地方。仓库在这欢迎 clone 下来自己npm run verify打我脸https://atomgit.com/gcw_QMYlF6Ie/fn-plot
返回列表