duangsuse::Echo
717 subscribers
4.26K photos
130 videos
583 files
6.48K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
回溯完成,得 2 正确。
每次由归纳程序 reduce 从栈上 pop 出一个项目
如果是 join,则 join 又会从栈上 pop 一个项目,作为 reduce 的 base

不过这样实际上就作了一个断言:栈后面的全是结合性弱的,而且这部分全都是往右结合

我相信这断言是正确的,因为如果有打破它的情况,早就在 stack.mapTop {} 的时候被结合了
Calc.kt
4.7 KB
新的,带基于栈的 infix 链扫描算法的 Calc.kt 例子
duangsuse::Echo
Calc.kt
这是上一个,只比它小 1K
现在是愉快的跑分时间~
我将随机生成 5个甚至2 个字符长度的 number,性能优化dssq。
2 个甚至 1 个的空白 ' '
[+-*/!] 里随机选的中缀

重复 5k 个这样的 0 {op whitespace num}
然后比较两个算法(递归 vs. 显式栈)处理的性能(时间开销)
Code: 70 - 96991 - 4464 / 0834 * 6187 - 80 * 75 + 18840 ! 1892 ! 77 - 16463 + 008 / 958 * 01 ... utf16-size=147KB
Launch parse using system stack 14032681694858
= 0
Finish parse using system stack in 14192923ns
Launch parse using ADT stack 14032696090696
= -138278
Finish parse using ADT stack in 1966603ns

好像自动生成的代码很容易 Division by zero…… 管它呢,我不会多做修改的(因为那要维护一个栈……)
结果是完全随机的,不过也很容易理解,就是里面哪怕有一个 *0,结果都是 0。

咱也看到了,的确是有优化的意味呢(迫真)
147KB 的输入,咱的算法足足比原算法快 (14192923-1966603) = 12226320 纳秒
也就是快 0.0122263 秒……

我突然觉得我做的很没意义,emmm

Code: 70395 * 748 - 090 * 984 - 29787 * 959 * 65 * 66 - 083 - 54 / 277 + 00 * 55860 + 7523 ... utf16-size=147KB
Launch parse using system stack 14506658662754
= 0
Finish parse using system stack in 13886095ns
Launch parse using ADT stack 14506672751297
= -1954497720
Finish parse using ADT stack in 2009080ns

第二次测试(在N次失败:栈溢出/Division By Zero后)ADT版依然比递归下降版快 11877015ns
也就是足足0.011877s
……在输入足足有147K的情况下
人急了是要上 TLS(ThreadLocalStorage) 的!
不过这是Kotlin/Native的…… 真是
显然这个东西不同平台要有不同实现(Kotlin/multiplatform)
不过因为这里是单线程使用,允许我偷下懒。
艹,Kotlin真骚,凭什么 timed 的 block 里面的错误 timed 外面的 catch 就抓不着了?
我很无奈,虽然咱的Kotlin不萌但是我依然要接受它
duangsuse::Echo
Code: 70 - 96991 - 4464 / 0834 * 6187 - 80 * 75 + 18840 ! 1892 ! 77 - 16463 + 008 / 958 * 01 ... utf16-size=147KB Launch parse using system stack 14032681694858 = 0 Finish parse using system stack in 14192923ns Launch parse using ADT stack 14032696090696…
对啊,我怎么没想到这两个版本对相同输入输出都不一样呢?肯定有一个是错的…… 这真是太残酷了
This media is not supported in your browser
VIEW IN TELEGRAM
我终于智障了, 不仅仅算法可能不正确,而且性能也辣鸡。
== summary ==
systemStack: 10 reports
of: 208.887micros, 130.996micros, 49.153micros, 146.203micros, 56.491micros, 47.472micros, 59.137micros, 20.813micros, 21.265micros, 46.551micros
min=20.813micros, max=208.887micros, mean=78.6968micros

AdtStack: 10 reports
of: 1.527514ms, 289.857micros, 71.758micros, 108.715micros, 117.732micros, 111.343micros, 124.688micros, 76.04micros, 48.207micros, 119.849micros
min=48.207micros, max=1.527514ms, mean=259.5703micros

ADT Stack 的性能一点也不比用纯递归的好,艹。
凭什么,难道他们有优化?
而且哪个是正确的?
不过说起来最近我真是随便写点代码都快能当框架用了…… 这是有毒么
劳资好不容易思考了半天的算法,居然发现性能还不如从前?那我设计它弄啥子,真是岂有此理了。
我要给 Calc.kt 加一个 Lexical Scoping 的功能,然后就可以有『人生第一解释器』啦~
我在想,其实递归下降法本身就很简单了,所以不一定必须弄太多Feeder什么的…… 还有『随机』MarkReset
很多文法都是LL(1)的,搞这么多纯属多余(虽然也不一定没用)

既然需要Mark/reset的时候很少,不如就只针对Peek进行扩展,让它能够随便(惰性地)支持MarkReset,不为那种File input的情况考虑了(反正PeekStream也足够高效了)
然后 SourceLocation 不一定非得Mixin,之前在ParserKt算行号的代码测试过可以拿到 SourceLocation 对象里作为它上面的方法,然后弄个(当然这次不会有所谓『Feeder架构』的屑问题了,只有一种Feeder)SourceLocPeekStream 把它包装起来,也不再同时兼容 CR, LF, CRLF,世界上哪有同时采用两种换行符的文字?Kotlin 支持 LF 或者 CRLF…… 那还是抽象出来让用户实现吧


什么镇静解析策略啊,就不要实现了,当然不是说递归下降实现不了,而是完全没必要实现 — GHC, Nashorn, Lua, Ruby 等主流语言/实现都没支持这种解析技术
而支持所谓『镇静策略』的Kotlin,效果和 GCC 一样不咋地,强迫自己吃屎一样,代码稍微错一点所谓的『镇静』就会弄出一大堆错误,根本不智能,真不知道这东西是设计过来干什么的。
『跳过』只会弄出更多岔子、『补上』你又有什么好补的?括号看起来是漏了可以补吗?除非一些显而易见的情况,当然我不是给IDE写解析器,所以不用支持完美的容错。

然后这次又是一个挂羊头卖狗肉的项目:Calc.kt,名字上是计算器;其实是支持Lambda Calculus的计算器,和一个解析器编写框架。
为了修复我花了那么大力气弄来的算法,我又和 Nashorn 测试了一下,暂时只看性能:

== summary ==
systemStack: 10 reports
of: 130.369micros, 27.851micros, 22.139micros, 24.31micros, 20.414micros, 21.36micros, 17.997micros, 19.12micros, 18.467micros, 19.574micros
min=17.997micros, max=130.369micros, mean=32.1601micros

AdtStack: 10 reports
of: 31.316551ms, 5.489379ms, 4.334858ms, 4.266494ms, 3.904404ms, 3.900235ms, 6.83763ms, 3.424587ms, 3.8487ms, 3.460497ms
min=3.424587ms, max=31.316551ms, mean=7.0783335ms

Nashorn JS: 10 reports
of: 342.638024ms, 15.991645ms, 18.379428ms, 11.238317ms, 9.294446ms, 7.796577ms, 7.543127ms, 7.239315ms, 6.986252ms, 5.68631ms
min=5.68631ms, max=342.638024ms, mean=43.2793441ms

—为什么 systemStack 那么快?因为我把测试逻辑都注释掉了…… 打住。
艹,我一个也没写对…… 全错了,我真的不敢相信
测试输入包含 5 个 token,也就是说是:
492 /  5912  / 40 * 2485 -  7688 * 94 *  26175  *  806  - 228  *  51 +  97779
这样。

Launch systemStack 29903450318121
= 0
Finish systemStack in 341.87micros
Launch AdtStack 29903455998170
= 132609292
Finish AdtStack in 1.790674ms
Launch Nashorn JS 29903458052996
= -1.524697469698283E13
Finish Nashorn JS in 334.887835ms

我找 Ruby 问了也是一个结果,这 TMD 怎么可能?
为了保证不是优先级的问题,我决定手工结合一下看看:

(( (((492 /  5912)  / 40) * 2485) -  (((7688 * 94) *  26175)  *  806) )  - (228  *  51) ) +  97779

=> -15246247231449

这么说我的解析算法写错了?

/usr/lib/jvm/java-1.8.0/bin/java... CalcMain
(( (((492 / 5912) / 40) * 2485) - (((7688 * 94) * 26175) * 806) ) - (228 * 51) ) + 97779
^D
886669351
emmm...
This media is not supported in your browser
VIEW IN TELEGRAM