2.3 数值函数:number 与 sum


2.3 数值函数:number 与 sum

本节摘要:XPath 里看起来像数字的文本仍是字符串,比较和求和之前要过 number()sum 对节点集里的值求和;floorceilingroundabs 做取整。空值和脏文本变成 NaN 后,比较全为假,不会抛异常——这是数值坑比字符串坑更阴的地方。

上手前先明确

阅读完本节,你应当能够:

  1. 用 number 把 price 转成可比较的数
  2. 用 sum 求价格总和,并说明 1.0 对节点集求和的写法
  3. 用 floor、ceiling、round 说明朝哪边取整
  4. 解释 NaN 如何让谓词静默失败

一、先把三本书的价格加起来

三本样本:30.00、29.99、39.95。原文集写过两条求和:

sum(/bookstore/book/price) sum(/bookstore/book/number(price))

在 lxml 里第一条常常已经能给出约 99.94,因为它按节点集的字符串值转数再加。把 number 写进路径在 1.0 里不一定合法——number 不能当步中间的节点测试。稳妥是 sum(/bookstore/book/price),或在 2.0 写 sum(for $p in /bookstore/book/price return number($p))。原文集那条 sum(bookstore/book/number(price)) 是 lxml 宽松解析下的示范,换 Java 标准 XPath 1.0 可能直接编译失败。我的建议:求和用 sum(路径到 price 元素),比较用 book[number(price)>35]

过滤贵书:

/bookstore/book[number(price)>35]/title

Learning XML。若写成 price>35 而无 number,1.0 会尝试把两边转成数再比;成功时看起来「也能用」,失败时(价格带货币符号 $39.95)整本消失。带单位的价格先在宿主里清洗,或 translate(price,'$','') 再 number。

二、取整与转换

函数 含义 例子 注意
number 转数 number('39.95') 空串、非数字 → NaN
sum 求和 sum(book/price) 参数须是节点集在 1.0
floor 向负无穷取整 floor(29.99) → 29 负数更要小心
ceiling 向正无穷取整 ceiling(29.99) → 30
round 就近,.5 向正无穷 round(29.5) → 30 与银行家舍入不同
abs 绝对值 2.0 更常见 1.0 无 abs,用条件绕

原文集 2.3 把 abs() 当作 2.0 数值函数介绍,签名带 numeric?。在只保证 1.0 的 lxml 里不要调用 abs。需要绝对值时,谓词拆成正负两支,或回宿主。

round 的 .5 规则是朝正无穷:round(1.5) 为 2,round(-1.5) 为 -1。不要用「四舍五入到偶数」的 IEEE 习惯套它。

⚠️ 常见坑:sum(book/price) 若某本缺 price,1.0 对该节点字符串值当 0 或忽略,视实现;缺元素的 book 根本不进节点集。缺的是元素还是元素为空,结果不同。空 <price/> 的字符串值是空串,number 为 NaN,sum 遇 NaN 会污染总和。
💡 关键直觉:NaN 不报错。谓词假、总和变 NaN,看起来像「数据被吃了」。先 number(price) 单独打出来看是不是 NaN。

图:比较前必须过数字门

图:比较前必须过数字门

三、1.0 没有平均值函数

要均价:sum(price) div count(price)。除法运算符是 div 不是斜杠——斜杠已经被路径占用。mod 取余。这两个词写进 mermaid 没问题,写进路径别写成 / 当除法。

2.0 增加 avgminmaxabs。Saxon 等引擎可用。lxml 里用宿主算 min/max 更省事:把 price 列表拉出来用 Python。

布尔与数字的交叉:true 转 number 是 1,false 是 0。几乎不要依赖这点做算术。反过来 boolean(0) 是假,boolean(NaN) 是假,boolean(1) 是真。

问题:为什么 book[price>number(title)] 没意思?

title 转数字几乎必是 NaN,谓词全假。比较要双方都是同一量纲。书名和价格放一起比,是类型没对齐,不是 XPath 坏了。

问题:整数除法吗?

5 div 2 得 2.5,不是 2。要整数再 floor。XPath 1.0 数字其实是双精度浮点,长整数会有精度问题,钱用分做整数或回宿主用十进制类型。

四、求和污染、除法单词、钱的精度

sum 遇到空 price 元素时,空串转数是 NaN,1.0 的 sum 被 NaN 污染后整笔总和不可用。防护是先筛有数字的书:sum(/bookstore/book[number(price)=number(price)]/price)——NaN 不等于自身,这个谓词能把脏值踢掉。看起来绕,但比事后在 Python 里发现总和是 NaN 再回头查哪一本脏更省时间。缺元素的书根本不进 book/price 节点集,和空元素是两类问题,count 一下 price 与 count 一下 book 对不上就说明有书没报价。

除法必须写 div。斜杠已经被路径征用。sum(price) div count(price) 是 1.0 的均价。5 div 2 得 2.5,不是整数除。要整数再 floor。mod 取余。这两个单词写进路径是运算符,不是函数调用,不要加括号当函数名。

钱用双精度会有 29.99 加 30.00 加 39.95 不一定显示成你心算的那两位小数。演示可以;对账不行。对账把金额当整数分,或回宿主用十进制类型。XPath 1.0 数字本质是浮点,这不是库的 bug。

floor 向负无穷:floor(-1.1) 是 -2。ceiling 向正无穷。round 的 .5 朝正无穷,round(-1.5) 是 -1。从 Python 的银行家舍入或「远离零取整」带着习惯过来会错。写单测覆盖负数。abs 在 1.0 没有,原文集 2.3 按 2.0 签名介绍它。lxml 里需要绝对值就拆正负支,或回宿主。

隐式转换会让 price>35 在干净样本上「能用」。一出现货币符号、千分位逗号、空格,转换失败变 NaN,比较为假,书蒸发。显式 number 不能防止 NaN,但能让你单独打印转换结果。加上 translate 去符号,再 number,是脏价格的最低防护。

五、三本价格怎么加,脏值怎么踢

三本干净价格 30.00、29.99、39.95。sum(/bookstore/book/price) 在 lxml 里大约 99.94。不要把这条写成 sum(/bookstore/book/number(price)) 当可移植写法,1.0 不允许 number 当步中间的节点测试,原文集那条是 lxml 宽松示范。比较用 book[number(price)>35],求和用 sum 指向 price 元素。

把第三本改成 $39.95。number 得到 NaN,price>35 为假,贵书列表变空或变短,sum 被 NaN 污染。单独对那本求 number(price) 能看见 NaN。translate 掉美元符号再 number,书回来。千分位逗号同样要先去掉。空 <price/> 与缺 price 元素不同:缺元素不进 book/price 集合;空元素进集合但值为 NaN。count 书与 count 价格对不上,说明有书没报价。

均价 sum(price) div count(price)。除法是单词 div。斜杠是路径。5 div 2 是 2.5。钱对账不要用这条均价当真,浮点会有尾巴。floor、ceiling、round 对负数的方向用单测钉死,不要靠口诀。abs 是 2.0,1.0 拆正负支。

练习:在样本里加一本没有 price 的书、一本空 price、一本带货币符号,分别 count、sum、过滤贵书,把三行结果记下来。以后看见「总和突然没有了」先找 NaN,不要先换引擎。隐式 price>35 在干净样本能用,这一组脏夹具会让它翻车,这就是为什么全文比较要写 number。

六、非数不报错只让书蒸发

干净价格能加出总和。带货币符号、空标签、千分位逗号,转换失败变成非数,比较全假,求和被污染。单独打印转换结果才能看见非数。缺元素的书根本不进价格集合,空元素进集合但值非法,两种病 count 书和 count 价格对不上就能发现。均价用求和除以个数,除法是单词不是斜杠。一点零数字是浮点,对账把钱当整数分。向下取整对负数朝更负的方向,半数规则朝正无穷,不要拿银行家舍入来套。绝对值是二点零,一点零拆正负。隐式比较在干净样本能用,脏夹具会翻车,所以全文显式转换。可移植的求和是把路径指到价格元素,不要把转换函数插在步中间当节点测试。

干净三本价格能加出总和。把其中一本改成带货币符号,转换失败变成非数,贵书列表变短或变空,总和被污染。单独对那本打印转换结果才能看见非数,先去掉符号再转,书回来。空标签和缺元素是两类病:缺的不进价格集合,空的进集合但值非法,计数书和计数价格对不上就能发现。均价用求和除以个数,除法是单词不是斜杠。低版本数字是浮点,对账把钱当整数分。向下取整对负数朝更负,半数朝正无穷,不要拿银行家舍入来套。绝对值是高版本才有。隐式比较在干净样本能用,脏夹具会翻车,所以全文显式转换。可移植的求和是路径指到价格元素,不要把转换函数插在步中间当节点测试。

原文集写过两条求和,一条直接对价格元素求和,一条把转换插在路径中间。后者在某种库里能跑,换到严格一点零可能编译失败。我主张求和指向价格元素,比较时再转数字。取整函数对负数的方向和半数规则与银行家舍入不同,要用单测钉死而不是靠口诀。低版本没有平均值函数,用求和除以个数,除法是单词。真转数字是一,假是零,几乎不要依赖这点做算术。反过来零和非数的布尔都是假。钱用浮点会有尾巴,演示可以,对账把金额当整数分或回宿主用十进制。脏价格不会报错,只是从贵书列表里蒸发,先打印转换结果看是不是非数。

夹具里同时放一本没有价格的书、一本空价格标签、一本带货币符号的价格,分别计数、求和、过滤贵书,把三行结果记下来。缺元素的书不进价格集合,空标签进集合但值非法,带符号的转换失败。以后看见总和突然没有了,先找非数,不要先换引擎。隐式大于在干净样本能用,这一组脏夹具会让它翻车,这就是为什么全文比较要写转换。均价用求和除以个数,除法是单词,斜杠已经被走路征用。五除以二得到二点五,要整数再向下取整。钱对账不要用这条均价当真,浮点会有尾巴。向下取整对负数朝更负,半数朝正无穷,从别的语言带着习惯过来会错,写单测覆盖负数。低版本没有绝对值,需要就拆正负支或回宿主。可移植的求和是路径指到价格元素,某种库宽松允许把转换插在步中间,换严格引擎会编译失败,不要把宽松示范当标准。

看见总和突然变成非法或贵书列表少了一本,先对每一本单独打印转换结果,找出哪一本是非数。货币符号、千分位、空标签、全角数字都是嫌疑人。先清洗再转,不要指望隐式转换帮忙。缺元素和空元素要分开数:价格节点数小于书数,说明有书没报价;价格节点数等于书数但总和非法,说明有空标签或脏文本。均价公式用求和除以个数,除法单词不能写成斜杠。浮点尾巴让演示总和不一定等于心算两位小数,对账换整数分。取整方向对负数和半数要用单测钉死。低版本没有平均值和绝对值函数,用算术组合或回宿主。可移植求和不要把转换插在步中间。脏夹具比干净样本更能教会你为什么全文显式转换。

非数不报错,只让书蒸发或总和脏掉。脏夹具里缺元素、空标签、货币符号三类要分开数,三类对策不同。

真转数字是一、假转数字是零,几乎不要拿这点做算术。反过来零和非数的布尔都是假,用计数再转布尔多一次算术,非空节点集已经是真。

三类脏价格对策不同:缺元素用存在性发现,空标签用非数发现,货币符号用去掉符号再转。混成一种清洗,总会留下一种漏网。

动手验收把求和与计数拆开看:价格节点个数小于书数,说明有书没报价;个数相等但总和变成非法,说明有空标签或货币符号。两类对策不同,缺元素靠存在性发现,脏文本靠单独打印转换结果发现。均价用求和除以个数,除法必须写成单词,斜杠已经被走路征用,抄成斜杠会变成另一步路径。

温故知新

  • 比较价格写 number:不要赌隐式转换
  • sum 吃节点集:把路径指到 price 元素
  • NaN 静默:脏文本让书从结果里蒸发
  • 除法是 div:斜杠是路径
  • 1.0 无 abs/avg:用算术组合或宿主
  • round 的 .5 朝正无穷:与银行家舍入不同

下一节补上开关:not、boolean、true、false、lang。没有 ! 运算符。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U