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
然后你接着往下输入 +1, 到扫描下一个 infix 的时候它要阻塞等待输入
最后你要回溯的栈就是这样 (1 +) ((2 * 3) + 1),得 8。
duangsuse::Echo
运行的时候就是这样
然后咱再来测试一下 0 + 1 * 2 ** 3 这种情况,假定 (**) 也是左结合的:
一确保算法在多个 Tail 同时出现的情况下(看起来)可以正常工作。
咱先改一下咱的Op定义,加入新的中缀

我们可以注意到,因为代码结构复用良好,所以没花多大力气我们就支持了一个新的中缀运算符(而且轻松解决了左右结合的问题)。

咱的词法分析器也不需要修改,因为它是这么定义的:

scanSpaces(); return if (peek in Calc.Op.keys) Calc.Op[consume()] else null
咱输入:0+1*2!3
的意思是:0 + (1 * (2 ! 3))

咱回溯的时候是这样的,
2 ! 3 (2%3)的结果是 2

看来咱写的代码不对……?
的确不对…… 0+2*3!4 明明应该成[Tail(0+), Tail(2*)] 的
噢是我定义新前缀的时候搞错了,本来是 ascending order 的 🐱
0+2*3!4!1

现在咱看看这个的临时栈
我们注意到,本来是 0+ 2*
(0+) 的提前和 2 弄混了…… 都是在扫描 rest 的时候搞的
看来还是思路错了…… Tail 是不可能被铺平的,除非你真不用伪递归(就是基于 Recursion<T> 的递归)
duangsuse::Echo
我们注意到,本来是 0+ 2* (0+) 的提前和 2 弄混了…… 都是在扫描 rest 的时候搞的 看来还是思路错了…… Tail 是不可能被铺平的,除非你真不用伪递归(就是基于 Recursion<T> 的递归)
突然觉悟了,为什么 Tail 里面必须是 Atom 啊?也可以是递归的 Tail 啊!
不过这样就等于是白建一个栈了……
我想到一种很自然的解决方法:

把栈表达为 [Base(0), Tail(+), Base(2), Tail(*), Base(3!4!1)] 的形式
然后我们回溯的时候修改一下策略,让它结合 lhs=pop() 就可以了
新程序!
好像没问题? (0 是 3!1 的结果)
0+1*(2!3) 准备回溯。
回溯完成,得 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)
不过因为这里是单线程使用,允许我偷下懒。