二分法查找时参数的写法
spl 提供了内存二分法查找,主要函数是 select@b(),还有针对主键的查找 find@b(),以及对有序文本文件的外存二分法查找,file(btx,csv,txt).iselect()。
select@b() 可以对序表、排列,数列、日期序列和文本序列进行二分法查找,只要保证对象升序排列。比如以下代码格中,A1 是一个只有 3 列的序表,一列文本,一列数字,一列日期,且 3 个列都是升序。如下所示:
A | B | |
1 | =26.new(char(~+64):key,~:val,date(2023,02,01)+~:dt) | |
2 | 序表查找文本字段 | 文本序列 |
3 | =A1.select@b(key=="F") | =A1.(key).select@b(~=="F") |
4 | =A1.select@b(key:"F") | =A1.(key).select@b(~:"F") |
5 | =A1.select@b(key>"F") | =A1.(key).select@b(~>"F") |
6 | =A1.select@b(key<="F") | =A1.(key).select@b(~<="F") |
7 | =A1.select@b(key>"F"&&key<="H") | =A1.(key).select@b(~>"F"&&~<="H") |
8 | =A1.select@b(key<="F"||key>"H") | =A1.(key).select@b(~<="F"||~>"H") |
9 | =A1.select@b(between@b(key,"F":"H")) | =A1.(key).select@b(between@b(~,"F":"H")) |
10 | 序表查找数字字段 | 数字序列 |
11 | =A1.select@b(val:6) | =A1.(val).select@b(~:6) |
12 | =A1.select@b(val-6) | =A1.(val).select@b(~-6) |
13 | =A1.select@b(val==6) | =A1.(val).select@b(~==6) |
14 | =A1.select@b(val>6) | =A1.(val).select@b(~>6) |
15 | =A1.select@b(val<=10) | =A1.(val).select@b(~<=10) |
16 | =A1.select@b(val>=6&&val<=10) | =A1.(val).select@b(~>=6&&~<=10) |
17 | =A1.select@b(val<6||val>=10) | =A1.(val).select@b(~<6||~>=10) |
18 | =A1.select@b(between@b(val,6:10)) | =A1.(val).select@b(between@b(~,6:10)) |
19 | 序表查找日期字段 | 日期序列 |
20 | =A1.select@b(dt:date(202302,10)) | =A1.(dt).select@b(~:date(202302,10)) |
21 | =A1.select@b(dt-date(202302,10)) | =A1.(dt).select@b(~-date(202302,10)) |
22 | =A1.select@b(dt==date(202302,10)) | =A1.(dt).select@b(~==date(202302,10)) |
23 | =A1.select@b(dt>date(202302,10)) | =A1.(dt).select@b(~>date(202302,10)) |
24 | =A1.select@b(dt<=date(202302,10)) | =A1.(dt).select@b(~<=date(202302,10)) |
25 | =A1.select@b(dt>date(202302,10)&&dt | =A1.(dt).select@b(~>date(202302,10)&&~ |
26 | =A1.select@b(dt<=date(202302,10)||dt>=date(202302,16)) | =A1.(dt).select@b(~<=date(202302,10)||~>=date(202302,16)) |
27 | =A1.select@b(between@b(dt,date(202302,10):date(202302,16))) | =A1.(dt).select@b(between@b(~,date(202302,10):date(202302,16))) |
28 | 序表查找多字段组合复杂表达式 | |
29 | =A1.select@b(key>="F"&&val<=15&&(dt=date(202302,15))) |
可以看到,针对序表、文本序列、数列和日期序列,参数中除了可以使用冒号和减号 :、- ,还可以使用常规的逻辑表达式 ==、>=、<=、&&、||,也可以使用 between@b(),甚至像代码格 A29 中针对序表的多字段组合的复杂表达式也可实现二分查找,很方便也很好用。
我的第一个问题是如何对序列的序列 (嵌套二层序列) 进行二分法查找? 使用时发现,对序列的序列使用二分法时,参数的写法上会有一些限制,不能使用常规的逻辑表达式,如下所示,A1 是一个嵌套序列,但每一列 (列的描述不准确,只是为了直观形象) 元素都是升序的:
A | B | |
1 | =26.([char(~+64),~,date(202302,01)+~]) | |
2 | 序列的序列用二分法查找时有以下限制: | |
3 | 1、表达式中不能使用 >,=,< | |
4 | =A1.select@b(~(1)=="F") | 需要返回值为整型的表达式 |
5 | =A1.select@b(~(1)>"F") | 需要返回值为整型的表达式 |
6 | =A1.select@b(~(2)==6) | 需要返回值为整型的表达式 |
7 | =A1.select@b(~(2)>6) | 需要返回值为整型的表达式 |
8 | =A1.select@b(~(3)==date(202302,10)) | 需要返回值为整型的表达式 |
9 | =A1.select@b(~(3)>date(202302,10)) | 需要返回值为整型的表达式 |
10 | ||
11 | 2、文本等于只能用冒号:(数字和日期可以用冒号和减号) | |
12 | =A1.select@b(~(1):"F") | 文本相等只能用冒号 |
13 | =A1.select@b(~(2):6) | 数值比较可以用冒号 |
14 | =A1.select@b(~(2)-6) | 数值比较可以用减号 |
15 | =A1.select@b(~(3):date(202302,10)) | 日期比较可以用冒号 |
16 | =A1.select@b(~(3)-date(202302,10)) | 日期比较可以用减号 |
17 | ||
18 | 3、大于等于小于等于只能用 between@b() | |
19 | =A1.select@b(between@b(~(1),"F":"A")) | 区间左大右小 大: 小 返回等于左边的值 |
20 | =A1.select@b(between@b(~(1),"F":"J")) | 闭区间正常表示 |
21 | =A1.select@b(between@lrb(~(1),"F":"J")) | 开区间 |
22 | =A1.select@b(between@lrb(~(2),5:8)) | 数字区间 |
23 | =A1.select@b(between@b(~(2),5:inf())) | 数字大于等于 |
24 | =A1.select@b(between@b(~(2),-inf():5)) | 数字小于等于 |
以上语句中 [A4:A9] 使用常规逻辑表达式是不能出结果的,会抛出错误“需要返回值为整型的表达式”,从 [A11:A24] 可以看到,对序列的序列使用二分查找只能用冒号或者减号或者 between@b(),并不像一开始提到的序表和序列的参数写法那么全面。所以,
问题 1:序列的序列使用二分法 select@b() 时,参数可否使用常规逻辑表达式,>、<、=、&&、||,实现序表、排列、序列、序列的序列在参数用法上的统一?
第二个问题是关于外存二分查找时的参数写法,file(btx,csv,txt).iselect(参数, 字段),其中的第一参数可以写成单值,或者一个有序的序列,或者用区间表达式 a:b。区间表达式可以写成 a:b 表示 a<= 字段值 <=b,也可以写成 :b,省略左边表示 字段值 <=b,或者写成 a:,省略右边表示 字段值 >=a。那如果要表示多种逻辑的组合呢,比如 字段值 <a || 字段值 >b,甚至是多个字段的复杂组合,按照目前的写法,似乎要写几次 [file.iselect( 条件 1),file.iselect(条件 2)…].conj(),所以,
问题 2:file(btx,csv,txt).iselect() 中的参数 1 可否使用常规逻辑表达式?
以上两个问题,恳请大佬们得闲时给予指导帮助,谢谢!
A.select@b(x) 原则上 x 应该是返回整数的表达式,对于相等就写成 f-v,范围查找就用 between@b(…)。
因为日期的减法运算默认是返回两个日期相差的天数所以日期字段可以写成 f-date(…) 这个样子,而字符串不能相减。
为了写法上的方便和效率问题,select@b 对 x 进行了智能识别,优化了 f==v、f>v1&&f<v2 这种查询字段值或者字段值范围的表达式,如果不能智能识别的并且 x 的返回值不是整数的则会报“需要返回值为整数的表达式”错误。
因为 spl 的表达式可以写的很复杂,是无法彻底智能识别的,对于不能只能识别的就用 between@b(…) 这种写法了。
iselect 的第一个参数只能是’值’或者’起始值: 结束值’
谢谢大佬解惑。
我想不明白序表和排列能用常规逻辑表达式,而序列的序列 [[],[],…] 不能用常规逻辑表达式。
既然序表能用,排列能用,那序列的序列应该也是水到渠成的事。排列也是特殊的序列。
为啥我会纠结这个二层序列,因为 txt 或者 xlsx 在读取时,由于某些原因要 @w 读取成序列的序列
我想着若能直接对这个二层序列运用二分法查找,就不用再多一步转换成序表后进行查找,
一来是方便,能一步不两步;二个是语法参数使用上的统一。
那二层序列用 between@b()时,如果是条件或 ||,只能是分开写之后 conj 了,[select@b(1),select@b(2)…].conj()
当然,序列的序列转换成序表也是简单的,E@b([[],[],[],…]),就可以用 select@b(针对 #n 的常规表达式)
或者,可不可以 notbetween(x,a:b) 返回的是 x<a||x>b 😄
目前或用 between 写不出来
😄 好的,谢谢!
不纠结了,序列的序列,就用 E@b([[],[]…]) 转序表,不自找麻烦了。😄
select@b() 对序表、排列、序列中常规逻辑表达式 ==,>=,<=,&&,|| 的智能识别优化,已经是个惊喜了。
🙏 🙏
大佬们😄 我再多问一句:
请问 spl 有没有计划优化一下 pselect@b() 二分法时参数使用常规逻辑表达式?
目前 pselect@b()参数的写法只支持冒号或者减号的等值比较,虽然也能用 between@b,但似乎 pselect@b(between@b(~,a:b)) 这样写的效率还不及常规的 pselect(~>=a&&~<=b)。而 select@b() 的参数优化了常规逻辑表达式的识别,效率确实可喜。
因为已有的二分法查找的优化代码是找出所有满足条件的,pselect 默认只返回一个,如果只找一个用优化代码可能更慢,所以只优化了 pselect@ba(>=a&&<=b) 这种查询。
代码已上传 github。
握艹,神速 666👍 👍 👍 谢谢大佬🙏 等更新…😄
谢谢大佬,pselect@ab()可以了……Holy High( 好厉害👍)