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
#Telegram var lastMsg = g.history[0]; val isAh={t:String -> t.all{it == "啊"} && t.length > lastMsg.text.length || t.length <= 3 } ; onNewMessage { if (it.user == lastMsg.user || isAh(it.text.replace("AhAhAh","啊")) ) +(it.delete()) else lastMsg=it }
Forwarded from 螺莉莉的黑板报
迷惑行为大赏

一个只能啊啊叫的群
@AhAhAhGroup

一个只能耸肩的群
@ShrugGroup
Forwarded from 螺莉莉的黑板报
格式工厂集成了 you-get 和 youtube-dl,这可太本土了 x2……
Forwarded from 螺莉莉的黑板报
格式工厂支持网易云 NCM 格式的加密音乐解码了,这可太本土了……
Forwarded from 螺莉莉的黑板报
格式工厂支持解码优酷私有 KUX 格式了,这可太本土了 x3……
草,暴力 try-catch 查找么 #tools

def trySearch(op, xs):
for x in xs:
try: return op(x)
except Exception: pass

def searchDecode(cs, text):
print("s = s.encode(%o).decode(\"utf8\")")
return text.encode(cs).decode("utf8")

def decode(text): trySearch(lambda cs: tryDecode(cs, text), encoding.charsets)

btn.onclick = ()=>ta.textContent=py["decode"](ta.textContent)
不过本群有一个初中大佬
如果一个选手比你强,还比你小,你就永远也赢不了他了
单 调 队 列 优 化
你可以看一下他GitHub
这是个OI超强,自己写编辑器的大佬!
duangsuse::Echo
借着这个官方手刃同人的机会,咱来谈一谈 Channel comment board 应该怎么写。 这里不提及回调、消息队列、按钮盘和内联之类的细节,只写成接受必要参数的 fun onXXX() 的形式 与 Telegram 的数据交流与存储责任优先提及: inline keyboard 各钮的文本和 callback ID 由tg保存,回调含 targetMessage bot PM(private message) 含事件 onMessageEdited, onMessageDeleted 单单…
现在脑子里完全是一团乱麻,我之前从没写过基于 Annotation Processor 的 codegen ,完全不知道怎么区别生成结构和复用结构更好

固然我是知道查询字段只要提供文本常量 ,但我不清楚倒底在哪区分不同底层(MutableMap/Mongo/SQLite) 的支持性,以及要弄个 channel 评论的 bot 而需要三个关系是不是认真的,毕竟靠 Grouping<BroadcastId, MsgId> 就可以反向查找到评论板和 getList ,那么利用关系型模型的价值到底在哪,是否要为这种情况支持原生后端

甚至,有没有必要对这玩意支持 JSON 序列化,从而用 tg 的 message 存储这类数据,如果要实现那那么多生命周期、细节我该怎么办?😱

不愧是最怕实际工程的 duangsuse ,希望以后有了 LiterateKt 的帮助我能多开点坑吧。
duangsuse::Echo
这两天我想实现最近设计的逆向计算表达式生成(就是把 +* 变 /- 的这种逆序+反向,只是未知只能有一个,具体要实现一个 findPaths { it !in globals } 细节不多说) 顺便也进一步理解了 ParserKt 最常用的结构们: Stmts (Stmt ';'|newline) -- elementIn Stmt ('L' Expr '=' Expr) | Expr -- OpChain.disambiguate, Seq(a,b,c) Expr opChain(Atom, "+- *\")…
我去看了下 Mivik 酱之前发的 kamet 编译原理实践博文,注意到他建模的 DFA 大概是 Array<List<Pair<CharRange,Int>>>
也就是每一点都类似 when(state) 0 -> 里 when(c) { in 'a'..'z'' -> state=1 } 这样

我的方案有点区别,是铺平的 Array<Triple<String,Int,Int>> (实际上肯定是 ArrayList ,因为动态测量状态数必须 a. 将函数直接构造 StateMover 变为构造中间结构,带来回收开销 b. 调用函数两遍,一遍只返回状态数来求和,类型上不优雅且做了无用功,结合其惯用法总体看不值得,就像 C string 的 strlen+strcat 也很弱智)

这种方法也能表示 DFA (好比把 when 转为嵌套 if ,一个节约时间一个节约内存),总体上我还是选择了把 when 转成多层 if elif,因为受不了那么多 MutableList (有点 Lua 的气质了…… 不敢用数组只会链表)

这个要反编译回 DFA 的话就要合并测试链了,如
("a", action(0), 1)
("b", action(1), -1)

就要变成 mapOf("a" to 0, "b" to 1) 这种,读取法是

readMap m (t, -p, n) { m[t] = p; readMap m cs[n] }

这个方法只能串联起 action (p<0) 的部分,唉反编译算法递归起来就是麻烦... 还好我不打算写

他也提供了一个方法手动建立 DFA ,当然也可以由 NFA 子集合并法优化而来,之后 Parser.kt 的写法是 LR(1) 也即递归下降法(left-right recursive descent, 不过这个缩写好像有 left recursive 的意思)

ParserKt 对此类算法的需求仅仅是 lexer 跳空格层面而已,所以没有严肃到独立实现一个 regex 匹配

谈到解析器框架的设计,函数式那边都是 StringView: get/length/drop(n) 和 Regex/Substring match 整个输入 String 作 lexer (因为 re 不支持字符流匹配) 的货色, ParserKt 算是两边的混血,拒绝函数式的啃本和工程派的复制粘贴

如今终于完美解决跳空格问题了,还是同时依仗了函数式的简洁和状态机的非阻塞,当然也离不开对部件重要性的正确评估(拒绝跑题),只有多范式互补才是 Kotlin 的正统啊!
duangsuse::Echo
我去看了下 Mivik 酱之前发的 kamet 编译原理实践博文,注意到他建模的 DFA 大概是 Array<List<Pair<CharRange,Int>>> 也就是每一点都类似 when(state) 0 -> 里 when(c) { in 'a'..'z'' -> state=1 } 这样 我的方案有点区别,是铺平的 Array<Triple<String,Int,Int>> (实际上肯定是 ArrayList ,因为动态测量状态数必须 a. 将函数直接构造 StateMover 变为构造中间结构,带来回收开销…
唉,现在看这个 Ruiko.kt (原版是一 F# 函数式大佬的) 还是搞不懂它拿 MutList 实现 lr 是什么个原理,看这架势大概要理解编辑撤销的 stack 指针和为何用 Set 吧。(估计理解了以后又会失望,索性不费这时间)

不过现在的我可一点也不想顶礼膜拜,因为俺明白了俺就是个农民,农民靠拿出切实可用、啥人都看得懂的玩意过活,不是靠智商和“设计模式”,唉。

俺设计的 ParserKt, 起名字都不往函数式、逻辑数学上靠,

是 Seq,Decide,Repeat(asXXX(), p), item,elementIn,satisfy,
Convert,Deferred,toConstant,toDefault
Piped,alsoDo,calm
(SDRies CDcdPAC)
SurroundBy, JoinBy, OpChain, TriePattern
(SJOT)
不是 And,Or,Repeat,Literal,Predicate,Except

也不是 tuple2,choice,many1,anyOf,sepBy,join,betweenStrings,sep_by

这个无聊的“DSL”根本不需要你去手动铺平乱糟糟地嵌套 infix 造的数据,再去解释它,它本身甚至作为必须地函数对象都不够格,因为太简单,直接对应菜包递归下降法,内联都可以。

毕竟俺们老农就是讲究不给人添麻烦,只解决问题不造成问题。

至于关键字开头的结构的解析,以及跳过空格的实际问题,俺设计了最多只用读一遍的 Piped.run(Trie<Pattern<T>>) 和 LexerFeed ,继续死磕不需要“词条”的纯解析器,真是违背了咱的 lex/yacc 祖师爷呢。

俺废了好大力气才想出几个能用名字,是因为俺蠢,所以只知不断重写代码,整理命名、顺序与数据归属和关键接口的提纲。

写 parser combinator 的大大们高,起的名字再随性、词性再乱也是函数式的禅语,俺们听不懂,厉害了。

贵圈有的人说 Rewrite 是 parse res. transform ,有人觉得 Rewrite 是把“非终结符” 换成“终结符”;有人觉得 lift 是从内作用域换到外作用域,有人觉得 lift 是从短数值长拓到长数值长,你们洋文太好了,程序变形也太牛了,抽象能力也高,好到等效的程序不知道复用只会拿蹩脚的方式重新表达了,那定是语序都不得看的计算机头脑。
还有拿 Lens 做 IO以及为讨厌的新技巧起莫名其妙的名字,一点提纲也不需要有,因为别人不懂蛮好嘛,懂得太容易怎么能算知识?就是这一点我老学不会,所以俺就变成了从函数式抄袭的土码农,真可悲呢。
This media is not supported in your browser
VIEW IN TELEGRAM
https://github.com/corsis/XParsec/blob/master/XParsec.fs#L99 看不懂。 看到 Sigma 我还以为是表达 Sum type (safe union) 的呢,真是无语╯﹏╰

不过多向输入的解析 ParserKt 就收下作 NonlinearFeed 了,虽然我不知道有什么用,但反正 state 和 BaseJoinBy 这种极端情况的 API 都有了,多加一个也正好能让 alsoDo 有用点(不过 Feed 为避免用 Character 泛型引用造成不必要的开销,看来只能把 Char 当成 Short int 用作索引了)

或许能 Repeat(asList(), XE.attr("font")).direct(XMLFeed.child) 会很酷(迫真)

不过能这样的确有点价值:

val defs = Seq(XE.tag("key").direct(X.next), Repeat(asList(), XE.tag("def"))).direct(X.child)
val root = Decide(defs, XE.anyElem)


说起来新 ParserKt 也支持左递归:

let digit = oneOf "0123456789" --> fun d -> int(d) - int('0')

let digits = production "digits" digits.rule
<- digits + digit --> fun (a, b) -> a*10+b
|- digit


val digit = Convert(elementIn('0'..'9'), -'0')
val digits = Piped.leftRec<Int> {
Decide(Seq(it, digit) { first*10+second }, digit)
}