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

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
http://www.yinwang.org/blog-cn/2017/05/25/dsl#动态逻辑加载
草,这个玩意放 Kotlin 里就是一个组织出 typealias Predicate<T> = (T) -> Boolean 的解析器,其实根本没必要做成独立解释器甚至逻辑关系式编程的……
duangsuse::Echo
呃... 既然都说到这一步了就顺带讲一下我之前关于 动态作用域/词法作用域的见闻,虽然我知道没人感兴趣的。 动态作用域(dynamic scoping)就是你们能想到的,末端是全局作用域的嵌套表。 理解为栈,当前层的key被赋值、最顶层的表里的key被取值,所以栈帧销毁后解析会有不同的结果。 动态作用域没什么问题,也被用于诸如(嵌套命名空间)类型名解析的地方,除非函数可以作为值——比如 (set! a 1) (def get-kst() (lambda () a) ) (def main(a) (get…
关于 Kamet 里 val/var 和 let 的区别,本来我的意思是要除掉 let 的,但它的语义也的确不同
最开始, Kamet 只有栈上局部变量 var 和 const var (即后来的 val)
后来 Mivik 可能是发现 fun add1(n:Int) { val res = n+1; return res } 完全不需要实际栈上分配而可以内联,于是又新加入了 let

看起来 var / val 和 let 是各司其职、其区分无可厚非,我相信这种做法仍是不好看的,并且 val 应该默认具有 let 的语义(Kotlin 一直为最常用的语法优化简洁性)
只有在定义聚合量的时候 val/let 的区分才有意义,因为标量都可以选择让 LLVM 自动处理寄存器分配(或溢出到栈上)

对于 C 系语言来说,没有 GC 、struct 数据的指针是暴露的,但是返回在函数内局部分配的指针是错误的悬垂指针,局部分配一个 struct 却要从堆上复制是无意义的,
val 「定义栈上变量」的语义除了 no initializer 外只有调用构造器(因为要局部分配初始化)的可能(当然 Rust 式的 {} 构造器也有可能...)
https://github.com/Mivik/kamet/blob/master/src/main/kotlin/com/mivik/kamet/ast/DeclareNode.kt#L58
所以可不可以把
val x: [Int, 5]
let first: &Int = x[0]

替换成
val new x: [Int, 5]
val first: &Int = x[0]

呢...
duangsuse::Echo
呃... 既然都说到这一步了就顺带讲一下我之前关于 动态作用域/词法作用域的见闻,虽然我知道没人感兴趣的。 动态作用域(dynamic scoping)就是你们能想到的,末端是全局作用域的嵌套表。 理解为栈,当前层的key被赋值、最顶层的表里的key被取值,所以栈帧销毁后解析会有不同的结果。 动态作用域没什么问题,也被用于诸如(嵌套命名空间)类型名解析的地方,除非函数可以作为值——比如 (set! a 1) (def get-kst() (lambda () a) ) (def main(a) (get…
我想这个泛型参数推导其实到底是我想复杂了吧…… 总觉的要用描述式编程来推导类型参数
仔细想想,一个局部 MutableMap 就可以了。 大家把自己关于某未知类型 <T> 的信息聚集到一块不就推出来了,何必用相等性双边推导(unification)来做呢....

class A<T>(val x: T)
val a = A(1)

直接像 Mivik 一样弄一个 TypeParameterTable.scoped , 让构造器参数的 x:T, T=Int 往表里一放 , 同时也解决了递归无法推导的问题
fun <T> extract(a: A<T>): T = a.x
extract(a)
同样是把 a:A<T>, T=Int 往表里一放就推出了 T,真的很无聊欸...
fun <T, R> run(a: A<A<T>>, op: Function<T, R>): R = op(a.x.x)
run(A(1)/*A<A<Int>>,A<Int>,T=Int*/) { print(it)/*(?) -> R=Unit*/ }
也同样很好推导,所有参数的类参名与实参一 zip,往当前表里一放再检查一致性,也不存在需要赋值 op的 <T> 的问题,用表惰性取值已经解决了

(难怪信息不够时 Kotlinc 只是简单的提及 no enough information, 因为这个算法本身就很简单...)

这么简单的推导算法那些 Javac 设计者竟然不弄完整?反而做成只有 "diamond operator" 的形式,他们真是弱智?!
This media is not supported in your browser
VIEW IN TELEGRAM
推个 Generic 类型还用相等关系解构去看什么 state var intro goal ,看来我受到的误导很深。
原来不必建模成相等关系然后 unification ,只用 table 也能做到啊,真的是把 information 聚在一起就能推出来,是我智障了。
(当然用相等关系但不必实现关系式编程也可以, Box<T> = Box<Int> 一样能推出 T=Int... 但基础组件写来麻烦一些)

Kotlin 里好像也是只有 fun 的类型参数能推导,而 class/interface 必须是显式写出的
不过今天填志愿,就不多说了。一般这种 "Talk is cheap" 的更新也不常见,改天我写个 bot 导出 markdown 发知乎去。

提出一个编程方法,没有任何特色而我将它用于无草稿设计,优点是设计小脚本很方便,称之为 LSPA(Lifecycle, structure, program action. 主要在 "LS", 加 "PA" 是为了好记)

比如小明有一个 pandas.DataFrame 要从网上搜索下载网页,测试关键词匹配数来生成 it["rank"] 列用于排序,这里就有两个 lifecycle: scrape-pages, gen-rank
分别使用 [psearcherr, requests, bs4], SavedFile(name, classifier), curl, findXXXLink 和 findMatches, xxxMatch, len 等函数来构成逻辑
df["rank"] = [sum(map(lambda tag: len(findMatch(SavedFile(name, tag).readOr(""), ["index", "a"]))  )) for name in df["name"]]

所以在整理好提纲以后,脚本编写会容易一些,但对大点的设计还是要逐步细分设计的。
用这种方法后可以将程序分为最基础的原理算法、某 lifecycle 里的功能点两部分(比如 scrape-pages 里可以指定 df[df["name"] == start:] 爬取行起点),方便进行隔离的建模
structure, program action 只是附加点,一般小脚本里的 structure 和控制流什么的不会太复杂
This media is not supported in your browser
VIEW IN TELEGRAM
from types import CodeType
c = CodeType(0,0,0,1,64,b'd\x00S\x00',(1,),('',),('',),'','<module>',1,b'',('',),('',))
eval(c)
嗯……找到问题所在了。 双向转换没写好,现在可以序列化缓存代码对象了。
完成了! 不过不知道是 pickle 性能高还是 struct 性能高... 反正我懒得写 pickle 版(花好大力气完成的复古
%timeit codeSer.loads(codeSer.dumps(c))
34.6 µs ± 103 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
from pickle import dumps, loads
%timeit codeSer.loadItems(loads(dumps(codeSer.dumpItems(c))))
25.8 µs ± 405 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

还好我写的抽象层有支持…… 就知道跑不过 pickle ... 这个变态腌黄瓜。 ☹️
This media is not supported in your browser
VIEW IN TELEGRAM
不想删啊…… 早知道用 struct 不合适的,而且它是个贼老的东西了,为了支持动态长度字符串我还加了 hack
性能渣是肯定的... 还是换 Pickle 后端好了
腌黄瓜实在是太过分了,我好不容易给 struct 弄好字符串支持的
早知道就不给弄了,草死了
duangsuse::Echo
完成了! 不过不知道是 pickle 性能高还是 struct 性能高... 反正我懒得写 pickle 版(花好大力气完成的复古
还有一个更过分的(主要是因为我不了解序列化的泛用性...)
from marshal import dumps 可以 dump Python 的 code object ,而且它就是用来生成 .pyc 文件的那个
虽然不能跨语言版本,但我要的代码缓存就是这个啊……

In [19]: %timeit marshal.dumps(c)
703 ns ± 4.27 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

而且它还超级快……
看来只有要压缩的情况下我写的代码才有一丢丢意义…… (它的存储比 marshal 的结果短... 1字节)
This media is not supported in your browser
VIEW IN TELEGRAM
struct,json,yaml,pickle,marshal 等序列化便利库我都用过,无非就是流 dump/load 和便利性 dumps/loads #Python #backend #bin

对于二进制读写的数值读写、字节序解释我也清楚的很, Kotlin Dokuss 和 SomeAxml(挂名) 项目都包含了基本二进制流和读写抽象

序列化(serialize)的用途一般是以二进制表示某种数据, marshal 和 pickle 虽然都是对象序列化,显然 marshal(封送) 更广义、更内部, C# 里也有这个名词,用于在进程间传递数据对象的深拷贝,也类似 android.os.Parcel
说起来王垠也真是日理万机,发封邮件他都不回😂 #statement

比冰封发了也不原谅或CovScript老李说了就直接踢更厉害呢……

我们就不应该相信大佬会为你一个人费时间,是吧?
真的好敬仰 RednaxelaFX 那样即既没有腔调、又能写出简单易懂文章的人,可惜他最近不在线了。
其实我最开始是以为王垠的许多观点和我一致,才借着回答文章里的问题的机会发邮件的。

其实王垠和我差太多了,我以为那些他表达的观点是我所认同的地方,都是他在谈大道理的地方。他自己当然没有做到这些观点的意愿,比如他说失败的经验很重要,但他从不提及初学者可能的失败;他教你区分知识的深度和广度,却从不告诉你还有什么有趣的书和链接;他夸某某教授与众不同、懂得大智若愚的道理,自己却“这个惹怒我了”、“那个费了很大力气、很有价值,不分享”

其实小白视王垠为大佬无非是他科普以及个性的确很好而已,或许也有清华辍学 title 的成分。

我把他视为大佬也无非是看到他弄过 PySonar/RubySonar 的类型推导语言工具、 Ydiff(树前序比较,这个我也写的出来...),以及那一百行 CPS(continuation-passing-style,假设所有call都不return)优化所谓无人写出的 Scheme 代码(这个倒没太大异议,毕竟 #Python 的红姐也最多实现了 PVM 的代码生成后端,没有 x86.)

其实见得多点的人都知道,知乎上 CS/PLT(程语理论) 大佬是很多的。王垠对 PLT 的理论科普太缺乏了(尽管他始终在做,但始终只有简单入门的部分),尽管他的博客也可以说是“业界清流”,也不是能让人完全满意的那种。

说到底就是肯做科普的人太少了,而且绝大部分 2~3 年资的大佬文章并不重视易懂性,显然是他们从不重视。 当然现在也还有星野大佬 blog.hoshino9.org 这种易读性可以的大佬存在,拭目以待吧。