duangsuse::Echo
王垠是谁
不过就拿知识哥韭菜这事我附议。这个人是没啥好评价的(哀其不幸怒其不争),不过他的行为和说法是嘈点满满
他当时对 #CS/#PLT 的科普还不错,也没删过文章,很可惜冰封在科普上没有和他平起平坐的能力(冰封老博客 乃至现在的博客都是自己的日记,他也不会增量更新旧博文),只能在小圈子有(尽管我们都算小圈)R大那样的人太少了(尽管R大也不能破圈///)
>这篇博文的核心是这一段:我早已感觉到长久以来科学界在掩盖真正的知识,把它们弄得越来越难懂,太多滥竽充数的人写一些垃圾论文。这就是我为什么抛弃了三个博士学位。这就是为什么我最近都在致力于把看似深奥的知识变得简单,并且传授给其他人。只有大部分人能理解科学和其他关键学科,才能确保它们不会腐败变质。
传授给其他人要是收费的
作者:kdurant
早年我还在知乎上吐槽过他的收费和intro,反被封了几天……
他当时对 #CS/#PLT 的科普还不错,也没删过文章,很可惜冰封在科普上没有和他平起平坐的能力(冰封老博客 乃至现在的博客都是自己的日记,他也不会增量更新旧博文),只能在小圈子有(尽管我们都算小圈)R大那样的人太少了(尽管R大也不能破圈///)
>这篇博文的核心是这一段:我早已感觉到长久以来科学界在掩盖真正的知识,把它们弄得越来越难懂,太多滥竽充数的人写一些垃圾论文。这就是我为什么抛弃了三个博士学位。这就是为什么我最近都在致力于把看似深奥的知识变得简单,并且传授给其他人。只有大部分人能理解科学和其他关键学科,才能确保它们不会腐败变质。
传授给其他人要是收费的
作者:kdurant
早年我还在知乎上吐槽过他的收费和intro,反被封了几天……
duangsuse::Echo
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Advanced_animations woc, Moz 这太厉害了,加速率弹球动画、alpha拖影合成,还包含位图处理,不愧是最知名的MDN啊,还有一个500行(上面 Java 版本)的渲染器 raycaster !太牛了吧,而且也提到了 ZIM和P5.js ,梦幻联动 为啥动苏喜欢画图呢 🤔 因为图形里有许多参数是可以抽提比率增减的。尽管看起来很傻(用矢量图软件更好…
👆结束。 王银的营销确实值得我们学习,他也是一个成功人士,在我看来他是比较吃香又能帮到一些人的;如果不是讨厌他对技术的随便,以及没有清华肄业的头衔,我或许想这样😂
#dev 现在的许多傲慢消失了,感觉好像啥也没专门去学一样😌
以前觉得组合器是特殊的,现在只是利用设计DSL
以前想长就是好、快,现在长就是烂、空洞
以前想多就是系统性,现在多就是冗余、套版
以前觉得CSS盒模型是坏的,现在发现它比其它平台更适合而贴切,言词不多,贵在扩展
以前想代码生成是大的,现在FileTree+列表处理 随手就能做
以前想反射是难的,现在发现只是名词太长,
以前解析器是不会的,现在提取前缀,组装返回值,预过滤行号流、记录span词区间 ,根本不需要分词器或任何工具和严谨
以前不知道UI框架里线程做什么,被 web 的 timers,worker post/onMessage 等 0阻塞API 矫正了,原来Thread级阻塞去 event loop 就是检测手势,查找调度Task 的,而并发异常只会随机出现; 队列是数据,随便在哪 add pop 阻塞poll;懂了为啥想放几音乐不能用for、想 fetch() 要暂断执行此函数 await 或.then(op)
以前没看到nav浏览栈是什么,被 web 的 PJAX 矫正了,原来可把生成View的数据保存pushState起来,也因此理解内存对象不都可序列化
以前不会HTTP和Cookie(服务端KV之K),现在只想大的API绑定,忽略无关数据交换的细节 httpd((req,res)=>res.write
以前不懂 SQL 和ORM,现在我只当它是严谨化加速列表处理过滤,以及事务和JOIN
以前连分页都觉得好神奇,现在
#PLT ”动态语言“ ”脚本“确实不严谨,但class{}大定义也是从小程序组合起来的,只在框架内思考会失去对框架本身的认知、放弃优化框架的可能。 例如反射只是元编程的运行时形式,«Ruby元编程»是本好书;编译期和运行期差别只在有无变量、编译解释只在有无缓存,代码生成是编译期计算。 强弱类型在于兼容和转换的显式隐式,类型推导可以让用户少手写编译期信息,类型检查能让人和机器少犯傻。 语法和语言工具用于表达,写法只有缘由,没有条条框框。
JS和CSS,JSON,YAML,HTML ,都是嵌套结构构成,只是人赋予了JS执行序、计算变量,而只让JSON解释为构造1数据--prettify又怎不是1数据 ;结构相同语法不同、数据相同命名不同,等于完全相同
效果相同类型不同 —泛型和 map,filter 函数正是如此
好的优化能让人知道。而不 prof, %timeit 怎么知道性能快慢
多观察或许就可预测,可预测或许就可控制。 被带偏或试着控制,各有好处,但没有理解缘故就只能被动模仿,束手束脚害怕出错。
就好像一个未良好复用的动作,能少一行代码,却会造就整个软件、更多软件里无数多余代码、不完善功能、高学习难度
以前不知模板和 animation/queue 是什么、粒子是为什么,现在我自己做过
以前不会二进制,现在 Read/Writer 流都被我抛了,我想0copy直接mmap()绑定,不停留在”Int=4x Byte“
以前不会画画,现在 arc rect fill stroke % alpha hsv(hsl) 一把梭,视频和参动之画也绘,了解向量计算=角度无关位置=(A-B)0点角,也认识到”非主流“ GLSL 打散绘制的方便
以前不知道值类型(全复制)、位置引用和惰性计算/编译期宏,和许多函数式Type术语,现在大致分清了
以前不懂编译原理,现在从 ASM chunkspy r2 libdl.so链接器 objdump shellcode 看到了更多、从 kamet 了解了 AST->LLVM::Value 树转化、地址和类型处理
从逆波兰了解了优先序重排和栈,从S-expr嵌套列懂了局部变量=栈位置 及()=>{} 闭包是程序+变量数据的实质;从无数次重写中明白了编程的实质是表达思维,多余代码只是环境限制
从函参返memo 了解了递归缓存与伪递归(递推) ;从深先树遍历了解了 DFS/BFS 途径/可达搜索,以及有更实际的 Dijstra/A*/JPS
从前缀树Trie+failPath=AC的关系了解了状态(自动)机、KMP的Map状数组了解了OI的一些惯用法,试出了1行的JS后缀合并树,从B站的一些视频明白了算法有许多讲法,之间差异也很大
从自顶向下,伪(左)递归转循环、去除流回退(peek/backtrack)到 LL(预判=k) 机; 到自底向上yacc/Simp?LR/LALR ,我不做无意义的打断优化;不把大状态机+报错,嵌套表等小儿科当编译原理。 别只用切碎的程序状态解释实现目的,复辟冯诺时代编译-汇编序列模板
以前很讨厌设计模式,现在会区分 Visitor,Delegate(T-by-obj,"proxy"),Observer 等真正的设计模式和
Chain(责任链),Adapter,Singleton(lazy),Builder(return-this链),Factory(选择子类new),抽象Factory(选中子类构造器), 等假装的设计模式和
Iterator,Context,Prototype(链上级虚表),Filter, SvcLocer(Lazy ctx+调用cache),
State(废话,啥函数不看this val),模板(废话,OOP封装继承是为啥的),策略(废话,OOP接口多态是吃白饭的),Command(函数闭包) 等非设计模式
明白 FP与OOP 间的异同,闭包即SAM对象,对象即 send(fname,*arg) 闭包
以前只会Java 甚至AWT/swing/net 都记不住,现在 JS/TS,Python,Kt 都是常用语言,也会 Ruby,C#/Powersh, C,C++,Scala/Hs ,我会在JS和Python 间按API和UI选择写脚本处理工具
以前害怕项目build定义,现在讨厌
以前觉得真理是系统性的,现在觉得真理也可以有开头和前言
以前相信教条规范,现在明白教条和权威也有局限性(比如不会自动化重构),需要择优
以前相信web是菜鸡聚集地,现在明白有人在写的 和你不是一个JavaScript
以前相信原理是极少数人在学的,现在明白任何人做同样的事情,也有选择与目标
以前觉得C难,现在知道C难点,以及导致难点的历史目标与时代局限性
以前只能看到app"前后端/桌面" ”小工具“的一亩三分地,现在明白嵌入式、各框架游戏、专业软件 比它们更有趣
以前尽量严谨,现在废话要简写,突出顺序和重点
我学到的最终不是知识,而是简化知识的方法
我编写的最后不剩代码,只是代码后的目的
以前觉得组合器是特殊的,现在只是利用设计DSL
以前想长就是好、快,现在长就是烂、空洞
以前想多就是系统性,现在多就是冗余、套版
以前觉得CSS盒模型是坏的,现在发现它比其它平台更适合而贴切,言词不多,贵在扩展
以前想代码生成是大的,现在FileTree+列表处理 随手就能做
以前想反射是难的,现在发现只是名词太长,
class{val/*field*/;@WTF fun} 没啥不好区别,换框架看文档对个名词就成以前解析器是不会的,现在提取前缀,组装返回值,预过滤行号流、记录span词区间 ,根本不需要分词器或任何工具和严谨
以前不知道UI框架里线程做什么,被 web 的 timers,worker post/onMessage 等 0阻塞API 矫正了,原来Thread级阻塞去 event loop 就是检测手势,查找调度Task 的,而并发异常只会随机出现; 队列是数据,随便在哪 add pop 阻塞poll;懂了为啥想放几音乐不能用for、想 fetch() 要暂断执行此函数 await 或.then(op)
以前没看到nav浏览栈是什么,被 web 的 PJAX 矫正了,原来可把生成View的数据保存pushState起来,也因此理解内存对象不都可序列化
以前不会HTTP和Cookie(服务端KV之K),现在只想大的API绑定,忽略无关数据交换的细节 httpd((req,res)=>res.write
以前不懂 SQL 和ORM,现在我只当它是严谨化加速列表处理过滤,以及事务和JOIN
以前连分页都觉得好神奇,现在
a.chunk(2)=[a.slice(0,0+2), a.slice(2,2+.. 和类似的列表处理写过无数;各种控件 tab,slideshow,fab,drawer,drop-upload 也早不稀奇,反而觉得还不够可配置#PLT ”动态语言“ ”脚本“确实不严谨,但class{}大定义也是从小程序组合起来的,只在框架内思考会失去对框架本身的认知、放弃优化框架的可能。 例如反射只是元编程的运行时形式,«Ruby元编程»是本好书;编译期和运行期差别只在有无变量、编译解释只在有无缓存,代码生成是编译期计算。 强弱类型在于兼容和转换的显式隐式,类型推导可以让用户少手写编译期信息,类型检查能让人和机器少犯傻。 语法和语言工具用于表达,写法只有缘由,没有条条框框。
JS和CSS,JSON,YAML,HTML ,都是嵌套结构构成,只是人赋予了JS执行序、计算变量,而只让JSON解释为构造1数据--prettify又怎不是1数据 ;结构相同语法不同、数据相同命名不同,等于完全相同
效果相同类型不同 —泛型和 map,filter 函数正是如此
好的优化能让人知道。而不 prof, %timeit 怎么知道性能快慢
多观察或许就可预测,可预测或许就可控制。 被带偏或试着控制,各有好处,但没有理解缘故就只能被动模仿,束手束脚害怕出错。
就好像一个未良好复用的动作,能少一行代码,却会造就整个软件、更多软件里无数多余代码、不完善功能、高学习难度
以前不知模板和 animation/queue 是什么、粒子是为什么,现在我自己做过
以前不会二进制,现在 Read/Writer 流都被我抛了,我想0copy直接mmap()绑定,不停留在”Int=4x Byte“
以前不会画画,现在 arc rect fill stroke % alpha hsv(hsl) 一把梭,视频和参动之画也绘,了解向量计算=角度无关位置=(A-B)0点角,也认识到”非主流“ GLSL 打散绘制的方便
以前不知道值类型(全复制)、位置引用和惰性计算/编译期宏,和许多函数式Type术语,现在大致分清了
以前不懂编译原理,现在从 ASM chunkspy r2 libdl.so链接器 objdump shellcode 看到了更多、从 kamet 了解了 AST->LLVM::Value 树转化、地址和类型处理
从逆波兰了解了优先序重排和栈,从S-expr嵌套列懂了局部变量=栈位置 及()=>{} 闭包是程序+变量数据的实质;从无数次重写中明白了编程的实质是表达思维,多余代码只是环境限制
从函参返memo 了解了递归缓存与伪递归(递推) ;从深先树遍历了解了 DFS/BFS 途径/可达搜索,以及有更实际的 Dijstra/A*/JPS
从前缀树Trie+failPath=AC的关系了解了状态(自动)机、KMP的Map状数组了解了OI的一些惯用法,试出了1行的JS后缀合并树,从B站的一些视频明白了算法有许多讲法,之间差异也很大
从自顶向下,伪(左)递归转循环、去除流回退(peek/backtrack)到 LL(预判=k) 机; 到自底向上yacc/Simp?LR/LALR ,我不做无意义的打断优化;不把大状态机+报错,嵌套表等小儿科当编译原理。 别只用切碎的程序状态解释实现目的,复辟冯诺时代编译-汇编序列模板
以前很讨厌设计模式,现在会区分 Visitor,Delegate(T-by-obj,"proxy"),Observer 等真正的设计模式和
Chain(责任链),Adapter,Singleton(lazy),Builder(return-this链),Factory(选择子类new),抽象Factory(选中子类构造器), 等假装的设计模式和
Iterator,Context,Prototype(链上级虚表),Filter, SvcLocer(Lazy ctx+调用cache),
State(废话,啥函数不看this val),模板(废话,OOP封装继承是为啥的),策略(废话,OOP接口多态是吃白饭的),Command(函数闭包) 等非设计模式
明白 FP与OOP 间的异同,闭包即SAM对象,对象即 send(fname,*arg) 闭包
以前只会Java 甚至AWT/swing/net 都记不住,现在 JS/TS,Python,Kt 都是常用语言,也会 Ruby,C#/Powersh, C,C++,Scala/Hs ,我会在JS和Python 间按API和UI选择写脚本处理工具
以前害怕项目build定义,现在讨厌
以前觉得真理是系统性的,现在觉得真理也可以有开头和前言
以前相信教条规范,现在明白教条和权威也有局限性(比如不会自动化重构),需要择优
以前相信web是菜鸡聚集地,现在明白有人在写的 和你不是一个JavaScript
以前相信原理是极少数人在学的,现在明白任何人做同样的事情,也有选择与目标
以前觉得C难,现在知道C难点,以及导致难点的历史目标与时代局限性
以前只能看到app"前后端/桌面" ”小工具“的一亩三分地,现在明白嵌入式、各框架游戏、专业软件 比它们更有趣
以前尽量严谨,现在废话要简写,突出顺序和重点
我学到的最终不是知识,而是简化知识的方法
我编写的最后不剩代码,只是代码后的目的
Telegram
duangsuse::Echo
#cplusplus #sql join 是按AB共同键的相等性过滤。中间三角是交、并、并-交
总之 join B on A.k=B.k 左交就是A+B查询,加 where B.key=NULL(仅选B没有的部分) 就是 A-B
所以L/R/inner/fullouter join 的复杂度是 O(nn)这种 for(a)for(b)
此外SQL 还有GROUP BY HAVING(=where)
总之 join B on A.k=B.k 左交就是A+B查询,加 where B.key=NULL(仅选B没有的部分) 就是 A-B
所以L/R/inner/fullouter join 的复杂度是 O(nn)这种 for(a)for(b)
此外SQL 还有GROUP BY HAVING(=where)
duangsuse::Echo
👆结束。 王银的营销确实值得我们学习,他也是一个成功人士,在我看来他是比较吃香又能帮到一些人的;如果不是讨厌他对技术的随便,以及没有清华肄业的头衔,我或许想这样😂
LL 是什么呢,我举道『编译原理(指模式匹配机)』题 (大写代表规则=非终结符)
在分词器即”不需要stack“的N/DFA状态机走完一步action,就拿到1token,喂给解析器;所有喂完没报错,应该就组织出了语法树(没错,不能用调用栈组织AST)
abc|adc 即 S=aX X=bc|dc
每LL(k)规则是1个或几个(规则/词条 序列);当规则前全部词条匹配,看
左递归 A=Bc B=a|Bx|By 可转为 A=ac|aXc X=x|y|xX|yX ;设想下没有 PEG 文法这是怎么执行的 😂 对了,PEG 可不是 CFG(无上下文,仅状态号)文法啊
一般递归下降读 r(op|uffl)e 括号里就是先试匹配,不能合并前缀,如果遇到 r(oom|oot) 这种(谁会写啊)的语法就不高性能了,所以将其LL(正向推导,LR是反向)化
手写递归的局限性在于 strP("abc") 在绝对前置有a 时无法取到 strP("bc") ,我们必须让输入流支持”回溯“=”peek“(不向前走),让 strP 重复比对流前缀 才能完成解析,如果能则称为 predictive(预测性) parsing ,而取消调用栈,手动while执行和st=转移(st); 但这里st是栈保存的 , 就变成正经 LL 😂
LL1 能解决前缀合并,LL1+左递归 还能取代逆波兰做优先级解析呢!(其实和 split('+').map( split('*') ).aryOrSingle 这种一样原理... 除了计算栈没一正经的,行为艺术。我还见过想比较相邻算符决定深浅,碰到 +*-^ 直接让(*)把右参抢了.. 枉费 Op(+,x0,rec(x1,*)) : rec(Op(*,x0,x1),+) 的写一大堆,民科了属于是
打表:
N\T|a b c d
S|1 0 0
bc|0 1 2 3
dc|0 0 4
LL语法G(k=1) 仅当 A → α | β 俩是独立规则:
ab不转为相同前缀. 例如 a=xy b=c1 c=xz 就不行
ab最多一项是"".
若β → t, then α does not derive any string beginning with a terminal in FOLLOW(A). a不转为 a的任何后项:单项
(妈的跟看天书一般,好几个符号没定义就使用,勉强猜了些
是不是LL(1):
S=SbA|aA
B=Sb
A=Bc
内联 S=aA|SbA A=Sbc
S=aSbc|Sba
有 S=Sa|Sb 是左递归,这不是,因此有可能
不存在S=A|B; A=ab B=ac —首/随符集含交点
不存在 A=a$a —首终结符有交点, 若规则含末尾匹配
S=aASb
A=aS|$
First(S ->aAS)={a} First(S ->b)={b}
First(A ->bA)={b} First(A ->ε)={ε} S的A内规则的first集 都不相交,满足条件2
(3) 由于ε∈First(A ->ε) Follow(A)=First(S)={a,b} Follow(A) ∩ First(A->bA) ) ≠ φ
作者:Joan Tsao
https://zhuanlan.zhihu.com/p/31301086 我是放弃了,给大家一个25赞大佬
感觉我自认的标签或许都该加上那么几个字:
”不纯的函数式“ ”不分词的编译原理“ ”不靠模式匹配的元编程“ ”看看就好的图形学“ ”不写代码的编程“(但写了一定要优雅
在分词器即”不需要stack“的N/DFA状态机走完一步action,就拿到1token,喂给解析器;所有喂完没报错,应该就组织出了语法树(没错,不能用调用栈组织AST)
tok=[],i=0;比如 ab ad ,首先匹配 a,它按 b|d 决策后 push 新规则
st = [S] // S 是根语法. LL(1)匹配
while((it=st.top) !=null)
if(it !in G)//终结符
if(t==tok[i++])pop(); //匹配一项
else 回退或error;
else //重写为规则
{pop(); push(G[it](tok[i]/*looKahead=1*/) ) }
abc|adc 即 S=aX X=bc|dc
每LL(k)规则是1个或几个(规则/词条 序列);当规则前全部词条匹配,看
G[it](tok[i])试着转移到新*序列*。左递归 A=Bc B=a|Bx|By 可转为 A=ac|aXc X=x|y|xX|yX ;设想下没有 PEG 文法这是怎么执行的 😂 对了,PEG 可不是 CFG(无上下文,仅状态号)文法啊
一般递归下降读 r(op|uffl)e 括号里就是先试匹配,不能合并前缀,如果遇到 r(oom|oot) 这种(谁会写啊)的语法就不高性能了,所以将其LL(正向推导,LR是反向)化
手写递归的局限性在于 strP("abc") 在绝对前置有a 时无法取到 strP("bc") ,我们必须让输入流支持”回溯“=”peek“(不向前走),让 strP 重复比对流前缀 才能完成解析,如果能则称为 predictive(预测性) parsing ,而取消调用栈,手动while执行和st=转移(st); 但这里st是栈保存的 , 就变成正经 LL 😂
LL1 能解决前缀合并,LL1+左递归 还能取代逆波兰做优先级解析呢!(其实和 split('+').map( split('*') ).aryOrSingle 这种一样原理... 除了计算栈没一正经的,行为艺术。我还见过想比较相邻算符决定深浅,碰到 +*-^ 直接让(*)把右参抢了.. 枉费 Op(+,x0,rec(x1,*)) : rec(Op(*,x0,x1),+) 的写一大堆,民科了属于是
打表:
N\T|a b c d
S|1 0 0
bc|0 1 2 3
dc|0 0 4
LL语法G(k=1) 仅当 A → α | β 俩是独立规则:
ab不转为相同前缀. 例如 a=xy b=c1 c=xz 就不行
ab最多一项是"".
若β → t, then α does not derive any string beginning with a terminal in FOLLOW(A). a不转为 a的任何后项:单项
(妈的跟看天书一般,好几个符号没定义就使用,勉强猜了些
是不是LL(1):
S=SbA|aA
B=Sb
A=Bc
内联 S=aA|SbA A=Sbc
S=aSbc|Sba
有 S=Sa|Sb 是左递归,这不是,因此有可能
不存在S=A|B; A=ab B=ac —首/随符集含交点
不存在 A=a$a —首终结符有交点, 若规则含末尾匹配
S=aASb
A=aS|$
First(S ->aAS)={a} First(S ->b)={b}
First(A ->bA)={b} First(A ->ε)={ε} S的A内规则的first集 都不相交,满足条件2
(3) 由于ε∈First(A ->ε) Follow(A)=First(S)={a,b} Follow(A) ∩ First(A->bA) ) ≠ φ
作者:Joan Tsao
https://zhuanlan.zhihu.com/p/31301086 我是放弃了,给大家一个25赞大佬
感觉我自认的标签或许都该加上那么几个字:
”不纯的函数式“ ”不分词的编译原理“ ”不靠模式匹配的元编程“ ”看看就好的图形学“ ”不写代码的编程“(但写了一定要优雅
知乎专栏
语法分析 | LL(1) 分析算法
在前面我们已经说过,LL(1) 也是属于自顶向下分析算法中的一种。 LL(1) 分析算法从左(L)向右读入程序,最左(L)推导,采用一个(1)前看符号分析高效(线性时间)错误定位和诊断信息准确有很多开源或商业的生成工具AN…
duangsuse::Echo pinned «#dev 现在的许多傲慢消失了,感觉好像啥也没专门去学一样😌 以前觉得组合器是特殊的,现在只是利用设计DSL 以前想长就是好、快,现在长就是烂、空洞 以前想多就是系统性,现在多就是冗余、套版 以前觉得CSS盒模型是坏的,现在发现它比其它平台更适合而贴切,言词不多,贵在扩展 以前想代码生成是大的,现在FileTree+列表处理 随手就能做 以前想反射是难的,现在发现只是名词太长,class{val/*field*/;@WTF fun} 没啥不好区别,换框架看文档对个名词就成 以前解析器是不会的,现在提取…»
duangsuse::Echo
LL 是什么呢,我举道『编译原理(指模式匹配机)』题 (大写代表规则=非终结符) 在分词器即”不需要stack“的N/DFA状态机走完一步action,就拿到1token,喂给解析器;所有喂完没报错,应该就组织出了语法树(没错,不能用调用栈组织AST) tok=[],i=0; st = [S] // S 是根语法. LL(1)匹配 while((it=st.top) !=null) if(it !in G)//终结符 if(t==tok[i++])pop(); //匹配一项 else 回退或error;…
王垠对CS科班编译原理侧重点的质疑是对的(然而并没暖用,就像教育也不只是为培育人才一样;你没法让所有人相信简单是对的
倒不如说是老师举例和对照的方法不好,LL 这样入门级的程序编号化还是比较好教的,可是他们一举例就是很生硬,非常莫名其妙的常量 也不先说目标,上来就“贴代码”、贴做的图(也不简洁、记流水帐),让人很迷糊:
③ T → FT ' FIRST ( T ) = { ( id }
④ T' → *FT ' |ε FIRST ( T' ) = {* ε }
⑤ F → (E)|id FIRST ( F ) = { ( id }
……
以下的内容全都是流水帐,没有任何解释和其他等价形式,学起来非常麻烦
https://zhuanlan.zhihu.com/p/70895051 这个人教的SSA就很好,公共子表达式消除CSE、常量折叠、死代码消除 都是SSA(执行序节点图)的直接优点,他对指令选择的解释”烧水,做菜,切菜 顺序..“一点没有专业人士的架子,对基本块BB x=1; if(q){k=1; a=k+x} else a=c+d; 里前 a 相当于 x+1 和放大SSA BB范围 x=1; if(!q)goto B; k=1;a=k+1; .. 还有范围分析,就说得好实际
”最近在做一个 x86 到 x86 的 jit 编译器…… 中间表示一般有两种形式,树:[+a t1=[+b c] d] 或者展开的序列表示:t1 = b + c; a = t1 + d;
……
当追踪不了的时候只要按照最保守的范围进行优化就能保证程序正确性。同时这也可以用于程序静态分析,比如未初始化变量的分析。
数据流分析可以对各种数据进行分析,比如在变量有别名(比如指针)的时候,纯粹的数值分析就会有问题,因为你并不知道指针会指向哪儿,这个时候就可以对指针所指的目标做数据流分析,继而对对应的数值进行分析。“
他还解释了这里的SSA形状
-相同的名字不出现两遍(无重赋值
-每个节点下面(最多)只有两个节点
-父节点的值直接依赖子节点的值
的原因:具体的说每一行只能有两个输入变量和一个输出变量,也就是 z = x op y,从树表示到结果,我们只要先子节点的遍历整棵树,在每个节点上输出一行对应的操作语句就好了。
在语义阶段结束后程序就变成统一IR组:跳转基本块BB,有入出口、前后项,分析都在基本块
https://zhuanlan.zhihu.com/p/47099755 然后这个 #ce #reveng 反编译原理也非常好。 如果按执行的方式把 if(q) a; else b; 即
推荐 http://zneak.github.io/fcd/2016/11/25/revisiting-regions.html
Dom树是入口到某一BB 的必经点 -及这些的必经点在前 -构成的树 ;最简解O(nm) 是枚举+BFS ,最常是 Iterative(20行 O(nn) )
https://blog.csdn.net/Dong_HFUT/article/details/121375025 列了一大堆实现,L-T支配树构造法本身代码比Dij搜索长3倍, O(logn+M)
两点互有路称强联通,然后还有强联通子集(分量,=SCC),tarjan 法(N+节点数M) 就能求一点的集
https://security.tencent.com/index.php/blog/msg/112] CFG Flatten 是利用运行期switch派发器的方法混淆代码 🤔都是些基于 CFG(控制流图) 的东西,对正常编程而言其实优化分析都是虚的,代码写好才最重要,不过这些还挺有意思的,至少比把程序打散为编号强
高级的科班编译原理除了龙书还有虎书鲸书,但语言的设计不需要懂太多知识,王垠在前也怼过 CFG analysis ,确实这些对编程并不重要,不通用的工具做着还是容易的,但它们也是个领域啊
倒不如说是老师举例和对照的方法不好,LL 这样入门级的程序编号化还是比较好教的,可是他们一举例就是很生硬,非常莫名其妙的常量 也不先说目标,上来就“贴代码”、贴做的图(也不简洁、记流水帐),让人很迷糊:
③ T → FT ' FIRST ( T ) = { ( id }
④ T' → *FT ' |ε FIRST ( T' ) = {* ε }
⑤ F → (E)|id FIRST ( F ) = { ( id }
……
以下的内容全都是流水帐,没有任何解释和其他等价形式,学起来非常麻烦
https://zhuanlan.zhihu.com/p/70895051 这个人教的SSA就很好,公共子表达式消除CSE、常量折叠、死代码消除 都是SSA(执行序节点图)的直接优点,他对指令选择的解释”烧水,做菜,切菜 顺序..“一点没有专业人士的架子,对基本块BB x=1; if(q){k=1; a=k+x} else a=c+d; 里前 a 相当于 x+1 和放大SSA BB范围 x=1; if(!q)goto B; k=1;a=k+1; .. 还有范围分析,就说得好实际
”最近在做一个 x86 到 x86 的 jit 编译器…… 中间表示一般有两种形式,树:[+a t1=[+b c] d] 或者展开的序列表示:t1 = b + c; a = t1 + d;
……
当追踪不了的时候只要按照最保守的范围进行优化就能保证程序正确性。同时这也可以用于程序静态分析,比如未初始化变量的分析。
数据流分析可以对各种数据进行分析,比如在变量有别名(比如指针)的时候,纯粹的数值分析就会有问题,因为你并不知道指针会指向哪儿,这个时候就可以对指针所指的目标做数据流分析,继而对对应的数值进行分析。“
他还解释了这里的SSA形状
-相同的名字不出现两遍(无重赋值
-每个节点下面(最多)只有两个节点
-父节点的值直接依赖子节点的值
的原因:具体的说每一行只能有两个输入变量和一个输出变量,也就是 z = x op y,从树表示到结果,我们只要先子节点的遍历整棵树,在每个节点上输出一行对应的操作语句就好了。
在语义阶段结束后程序就变成统一IR组:跳转基本块BB,有入出口、前后项,分析都在基本块
https://zhuanlan.zhihu.com/p/47099755 然后这个 #ce #reveng 反编译原理也非常好。 如果按执行的方式把 if(q) a; else b; 即
q ?not B; a; ?jmp C; B:b C: 切BB再反编译规约,(符号执行)只能得到q为true 或false 的语法树,较好的方法就是 Node Split ,即对两个枝都继续,匹配出 if 结构再规约出表达式(纯符号执行估计不行.. 嵌套几个if就会爆炸吧(引文 Handling Irreducible Loops),此外 DJ-graph (支配前驱|可能前驱) 的BB切分也比较好推荐 http://zneak.github.io/fcd/2016/11/25/revisiting-regions.html
Dom树是入口到某一BB 的必经点 -及这些的必经点在前 -构成的树 ;最简解O(nm) 是枚举+BFS ,最常是 Iterative(20行 O(nn) )
https://blog.csdn.net/Dong_HFUT/article/details/121375025 列了一大堆实现,L-T支配树构造法本身代码比Dij搜索长3倍, O(logn+M)
两点互有路称强联通,然后还有强联通子集(分量,=SCC),tarjan 法(N+节点数M) 就能求一点的集
https://security.tencent.com/index.php/blog/msg/112] CFG Flatten 是利用运行期switch派发器的方法混淆代码 🤔都是些基于 CFG(控制流图) 的东西,对正常编程而言其实优化分析都是虚的,代码写好才最重要,不过这些还挺有意思的,至少比把程序打散为编号强
高级的科班编译原理除了龙书还有虎书鲸书,但语言的设计不需要懂太多知识,王垠在前也怼过 CFG analysis ,确实这些对编程并不重要,不通用的工具做着还是容易的,但它们也是个领域啊
知乎专栏
编译器优化了什么
[经 @污博士 提醒,我决定把题目改了…]最近在做一个 x86 到 x86 的 jit 编译器,因之前大多接触的是编译器前端知识,所以重新学习了一遍中后端原理,做个整理,也欢迎交流拍砖。 这里不打算涉及具体的工程细节,…
但是像这个就很离谱了(#zhihu by Gene,#5)。
图1在说 F=(Lit val)|'('E')'
|(E T |E+T)|(T F|T*F) —加,乘
他还特意写成 E=E1+T ?这是什么意思(分支.语法属性写很长
语法属性=综合a+b|继承{let a=1; a+1}-上下文 ;这种废话根本是因为没有良好区分 text 和 AST/IR form 才导致的,因为 AST 级就是语言内数据了,谁会用写字作画的方法描述语义?直接上代码跑起来了,还有空讨区分继承和综合eval()? Visitor后序遍历,求子项前置的步骤不叫继承?
图2 就是继承,只是这个人和 ANTLR 一样,都没好好分清文法和语义需求值的关系,逗号是不存在的,它只是种校验,煞有介事把它作为一个居中节点... 链表都用了,还嫌求值不够乱吗?
#zhihu 许阳 的LL,LR 里写:
“ 前两天有朋友评论,工业界编译器在使用什么分析器。这个问题并不想看起来那么简单,虽然我们有许多parser generator,如yacc menhir anltr,但往往它们只支持某一种分析算法(当然,这对于非编译向程序是够用的)。实际使用时,我们可能会想要结合LL与LR:得到一个有算法层次的parser。这意味着我们很可能不能借助parser generator。
至于原因:一般,运算符的复杂优先级关系会需要LR文法,但high level的语法结构往往只需要LL文法,前面提到,LL文法有一些好处,尤其对于错误信息的报告。一门语言的语法设计,会遵循类似歧义的层次,high level很难有歧义。”
ANTLR不够用?然而他在评论区仍说LL1就够了,真是舍本逐末的编译原理;靠这样的做法, 你还想做编译器?其实不做 parser 都能写 codegen 了,编译就是形式扁平化 ,非得带这种,那一 toy语言不分分种变1年开发大项目!
一些人中文都嗦不利索就开始想计算机语言, 难道不能用递归下降优化的吗?好像正常前缀提取return 或逆波兰有问题一样,就不想想别的做法。
只知状态机不知正经编程手段,又不是为学习目的,让我想到华为方舟IR 里人说那个2k行代码的手写 parser... 我类个去, 这都2022年了,编译目标都是汇编了,还在手写汇编解释器呢!有处理Linux源码的性能,也没编译它的资格啊,你手写C语法状态表写猴年马月去
图1在说 F=(Lit val)|'('E')'
|(E T |E+T)|(T F|T*F) —加,乘
他还特意写成 E=E1+T ?这是什么意思(分支.语法属性写很长
语法属性=综合a+b|继承{let a=1; a+1}-上下文 ;这种废话根本是因为没有良好区分 text 和 AST/IR form 才导致的,因为 AST 级就是语言内数据了,谁会用写字作画的方法描述语义?直接上代码跑起来了,还有空讨区分继承和综合eval()? Visitor后序遍历,求子项前置的步骤不叫继承?
图2 就是继承,只是这个人和 ANTLR 一样,都没好好分清文法和语义需求值的关系,逗号是不存在的,它只是种校验,煞有介事把它作为一个居中节点... 链表都用了,还嫌求值不够乱吗?
#zhihu 许阳 的LL,LR 里写:
“ 前两天有朋友评论,工业界编译器在使用什么分析器。这个问题并不想看起来那么简单,虽然我们有许多parser generator,如yacc menhir anltr,但往往它们只支持某一种分析算法(当然,这对于非编译向程序是够用的)。实际使用时,我们可能会想要结合LL与LR:得到一个有算法层次的parser。这意味着我们很可能不能借助parser generator。
至于原因:一般,运算符的复杂优先级关系会需要LR文法,但high level的语法结构往往只需要LL文法,前面提到,LL文法有一些好处,尤其对于错误信息的报告。一门语言的语法设计,会遵循类似歧义的层次,high level很难有歧义。”
ANTLR不够用?然而他在评论区仍说LL1就够了,真是舍本逐末的编译原理;靠这样的做法, 你还想做编译器?其实不做 parser 都能写 codegen 了,编译就是形式扁平化 ,非得带这种,那一 toy语言不分分种变1年开发大项目!
一些人中文都嗦不利索就开始想计算机语言, 难道不能用递归下降优化的吗?好像正常前缀提取return 或逆波兰有问题一样,就不想想别的做法。
只知状态机不知正经编程手段,又不是为学习目的,让我想到华为方舟IR 里人说那个2k行代码的手写 parser... 我类个去, 这都2022年了,编译目标都是汇编了,还在手写汇编解释器呢!有处理Linux源码的性能,也没编译它的资格啊,你手写C语法状态表写猴年马月去
duangsuse::Echo
#bilibili >那你现场写代码😄 回复 @iiicp :以上是()和算数表达式的求值栈生成,加起来10行。 活动期分析后就可以临时变量到寄存器,或者干脆每个+-用既有栈变量。 当然,C会比JS多费不少代码 但我是认真觉得40分钟太长。如果真的细节到此程度,你可以加提纲,先说说要写啥、涉及啥流程和修改,再放PPT 你能火是我想不到的,第一期视频我就认为会石沉大海。因为我也会写些涉及文本结构和计算机绘制的东西,在B站这些很少,因此更要注意可重看性。 如果你打算更100天,回头发现这些录屏复习起来…
我tm 5行代码写完AST和表达式链(Lua同款),要是复杂也肯定是在尊重原理、尊重统一的前提下提升可读性和加入语言特性
LL左递归都不能解决中缀算符优先级,但LR可以? 连着来不行,打碎了可以(no backtrack); 正着来不行,反着就可以(bottom up, LR);而且只有这样可以,其他做法我不看? 贵圈是什么逻辑,而且 ANTLR 那么重都不能解决需求了?自己打自己啊。 GitHub 上排名前位的脚本语言没一个用 compiler compiler 的,有的甚至在用函数组合解析器😐😐
编译器不是编译原理的产物,它是计算过程表示形式的礼物。编译器也是正常软件,任何程度的应用都可以包含编译器能依靠的算法,而RegExp 多次exec也不是分词器特有或唯一选择,就像一种翻转的写法可以作为思维游戏,但不宜到处乱贴、霸占新手的精力
*LL是把(栈顶,初始=文件级)规则peek(1),替换为(规则|词条)序列,测试词条相等到下个规则;LR是把规则最右自(常量级)逐步匹配规约 直到顶规则. 它们的前项 预测解析器和SLR简单点
*无论LL或LR, 现行教程笔记都是非常俗套的,比N/DFA里更稀奇古怪的希腊字母和不先声明的示例张口就来,一大堆毫无特点的示例数据充斥整篇文章,更有C语言和GLSL臭名昭著的单复数混淆 等未有说明,到最后都不懂“规则” “非终结符”到底是啥结构,只晓得他是怎么完成了不起的 FIRST FOLLOW TERMINAL 表转化,连原因都不懂就要学做法
*CPy以前就是缓存解析位置结果避免 AB|A 即A(B)?时回退,这也是函数式常见的方法;但其实语法里会回退的情况并不多。词条peek/take=用调用栈LL1,也方便报错。
let a="(1 (2 3))".split(/([()\s])/g).filter(s=>s!=''),i=0;Mivik的kamet 也是用OOP子类和 Kotlin 的双this
层=(tk)=>{for(let x,q;x=a[i];){i++;if(x==')')break;q=x=='('; tk.push(q?(x=[]):x); if(q)层(x)} }
符链=(s,l={['+']: 1,['*']: 2})=>{let a=[],add=x=>a.push(x),
o,x=()=>add(Number(s())),
one=ed=>{x(); for(o=s(); l[o]>=l[ed];)one(o); add(ed)}
one('');a.pop(); return a
}
层(树根=晚执行=t0=[])
class XNode{ override fun Context.cg():llvm.Value } 求值,实现了很标准的仿rust语言,还支持多态和泛型、类型推导;除了 types&struct ,我都没见到啥和文本处理有关的,除了lexer是专门仿正则实现的DFA action,靠继续lex() peek/take和手写parser交换。 啊?有那么多解析器吗,编译器=解析器?没听说过啊!LL左递归都不能解决中缀算符优先级,但LR可以? 连着来不行,打碎了可以(no backtrack); 正着来不行,反着就可以(bottom up, LR);而且只有这样可以,其他做法我不看? 贵圈是什么逻辑,而且 ANTLR 那么重都不能解决需求了?自己打自己啊。 GitHub 上排名前位的脚本语言没一个用 compiler compiler 的,有的甚至在用函数组合解析器😐😐
编译器不是编译原理的产物,它是计算过程表示形式的礼物。编译器也是正常软件,任何程度的应用都可以包含编译器能依靠的算法,而RegExp 多次exec也不是分词器特有或唯一选择,就像一种翻转的写法可以作为思维游戏,但不宜到处乱贴、霸占新手的精力
*LL是把(栈顶,初始=文件级)规则peek(1),替换为(规则|词条)序列,测试词条相等到下个规则;LR是把规则最右自(常量级)逐步匹配规约 直到顶规则. 它们的前项 预测解析器和SLR简单点
*无论LL或LR, 现行教程笔记都是非常俗套的,比N/DFA里更稀奇古怪的希腊字母和不先声明的示例张口就来,一大堆毫无特点的示例数据充斥整篇文章,更有C语言和GLSL臭名昭著的单复数混淆 等未有说明,到最后都不懂“规则” “非终结符”到底是啥结构,只晓得他是怎么完成了不起的 FIRST FOLLOW TERMINAL 表转化,连原因都不懂就要学做法
*CPy以前就是缓存解析位置结果避免 AB|A 即A(B)?时回退,这也是函数式常见的方法;但其实语法里会回退的情况并不多。词条peek/take=用调用栈LL1,也方便报错。
GitHub
kamet/Parser.kt at master · Mivik/kamet
kamet - a simple programming language written in Kotlin. - kamet/Parser.kt at master · Mivik/kamet
duangsuse::Echo
王垠对CS科班编译原理侧重点的质疑是对的(然而并没暖用,就像教育也不只是为培育人才一样;你没法让所有人相信简单是对的 倒不如说是老师举例和对照的方法不好,LL 这样入门级的程序编号化还是比较好教的,可是他们一举例就是很生硬,非常莫名其妙的常量 也不先说目标,上来就“贴代码”、贴做的图(也不简洁、记流水帐),让人很迷糊: ③ T → FT ' FIRST ( T ) = { ( id } ④ T' → *FT ' |ε FIRST ( T' ) = {* ε } ⑤ F → (E)|id FIRST ( F…
https://zhuanlan.zhihu.com/p/99509681 #reveng #recommended #ce 虚拟机壳都弱爆了,不影响性能的OLLVM多好(那自动重构命名和类全名 的岂不是仍不够强 草..
在纵向70% state= angr汇编符号执行, -3行: 若执行踏入了 相关块addrs, 返回其目标地址,否则忽略此分支(消除无用块. hook地址是块内最后call)
然后会修复真实块间加jmp
把假条件 cmovz(zf?1:2) 改成jz +jmp 后随地址
在纵向70% state= angr汇编符号执行, -3行: 若执行踏入了 相关块addrs, 返回其目标地址,否则忽略此分支(消除无用块. hook地址是块内最后call)
然后会修复真实块间加jmp
把假条件 cmovz(zf?1:2) 改成jz +jmp 后随地址
https://zhuanlan.zhihu.com/p/385042207 #ce
看完我算是满意但又比较失望的。 其实他的AST可视化(是用 pyecharts. 一个children;text 的结构)做得好,但就最开始的那篇博文就走偏了
我以为那个计算机是逆波兰栈的(据说现在一些大学这样教, iseki 表示他上过课来问群朋友,当时吓得我以为是几百行代码才能做,赶紧抱紧那个民科优先级算法... 😂)
他的计算器只能算 "1+2", (很可惜递归下降不适合解决优先级) 但把分词/解析的职责和求值流程教了下,感觉鸡肋(因为30行parse()就算个加法..)。我不满意的是这个Python 实现没有简洁性, interpreter 会构造大量 dataclass ,故这个人每期代码都4,5百行
https://zhuanlan.zhihu.com/p/24035780 这是 #JS React 的爱好者写的,他举的 Expr+Factor=>Expr+'('+Expr+')'=>1+(2*3) 展开和 if Expr then BB (else BB)? 很实际,Java 里 if() if()a;else b; 的else是就近原则,但 LL里要写明 if() WithElse else Stmt 这样
然后左递归 A='a'|A'b' 就是 A='a'|'a'A1 ;A1='b'A1|null
可惜这种写法有多少层优先级就要有多少调用栈,在 Kotlin 里都有快10种,规则不重要,最终代码你要全部手写我敬你 😐
而且组织AST的方法也有点怪,是用
最后 visit 的方法也是依赖 + * 的层次性的,不可能推广到同层次 +-或 */ ,命名也不简洁
200赞。。。应该说在流行编程界教这些东西会更好…… 贺师俊 hex老也来了
https://zhuanlan.zhihu.com/p/208906640 #Go .这个的原理一样,但解析时求值了
https://zhuanlan.zhihu.com/p/331794661 他们的那套编译原理的真理论( 🤷♂️
https://zhuanlan.zhihu.com/p/26660940 #JS 这个和我最近设计的一个有点像... 不过也不够简洁(20行深拷贝... 30行都能实现miniKanren了),但非常直白
#learn 那我就讲下 N/D Finite Automaton (状态机,别瞎听他们加绝对前缀,没意义),下简称 N/D
N是一个状态列表,从列表:起始/begin状态号到含完成/final 状态,一次匹配就成功。 每状态有 CharClass 对应,成功则转移到许多?状态,否则原位
abc , a[be]c , \d\w+ 都可写成字符类的状态机,^$ 蛮特殊的,它断言当前在输入首尾
N不如D适合 match ,N的所有状态是平级的,ab?c 时状态会回退a,类似 when 变 if-else 会更实际些f
a[bc]*$ 里, (S0 a (S1 [b b1-S1] [c c1-S1] ) $) b,b1 这些"连接态"是等价的,a和$ 是等价的
把 通过S0通过a 能走到(transit)的态记为 q1, q1通过b 滤出q2 ,而只要q里包含 finalCell 就是结束态 ,继续下去可把N转为D。
“ 在这里是 {n5} 。之后求它的边界, 即每一个元素都通过 ε 走到能走到的所有状态,记为 q1 的闭包。” ... 其实就是BFS/DFS
“不动点算法:算法可以提前运行终止,因为状态数是有限的” ...
在mivik的正则库里,
在流程构造里 begin,end 是常被extend()的, link(cell, final)是状态尾部
repeat(1..2) 是最有趣的,repeatAtLeast 只是把 m.copy() extend N遍然后 m*,rep(a..b) 则是在a后放 b-a 个可跳end 的单元,遇到不在内的自然就跳出了
讲不完... 🤪 没有精力了,图形学应用还鸽着呢
正则能不能直接变DFA?
最魔怔的就是直接替换为规则集的规则 a=b|c|$ ;bc=c;cb=b; 还有cb$ ,'b' 本身没喂对可以不转移,也能等价它link到的字,这是什么构造.. 1字符1转移都没会还要整理这个 🤪
看完我算是满意但又比较失望的。 其实他的AST可视化(是用 pyecharts. 一个children;text 的结构)做得好,但就最开始的那篇博文就走偏了
我以为那个计算机是逆波兰栈的(据说现在一些大学这样教, iseki 表示他上过课来问群朋友,当时吓得我以为是几百行代码才能做,赶紧抱紧那个民科优先级算法... 😂)
他的计算器只能算 "1+2", (很可惜递归下降不适合解决优先级) 但把分词/解析的职责和求值流程教了下,感觉鸡肋(因为30行parse()就算个加法..)。我不满意的是这个Python 实现没有简洁性, interpreter 会构造大量 dataclass ,故这个人每期代码都4,5百行
https://zhuanlan.zhihu.com/p/24035780 这是 #JS React 的爱好者写的,他举的 Expr+Factor=>Expr+'('+Expr+')'=>1+(2*3) 展开和 if Expr then BB (else BB)? 很实际,Java 里 if() if()a;else b; 的else是就近原则,但 LL里要写明 if() WithElse else Stmt 这样
然后左递归 A='a'|A'b' 就是 A='a'|'a'A1 ;A1='b'A1|null
可惜这种写法有多少层优先级就要有多少调用栈,在 Kotlin 里都有快10种,规则不重要,最终代码你要全部手写我敬你 😐
而且组织AST的方法也有点怪,是用
let p1=new Node("Mul"); p.push(p1); readTerm(); p=p1; ,没有用参数 也没提及 p=p1; 得每次readXX 重设最后 visit 的方法也是依赖 + * 的层次性的,不可能推广到同层次 +-或 */ ,命名也不简洁
200赞。。。应该说在流行编程界教这些东西会更好…… 贺师俊 hex老也来了
https://zhuanlan.zhihu.com/p/208906640 #Go .这个的原理一样,但解析时求值了
https://zhuanlan.zhihu.com/p/331794661 他们的那套编译原理的真理论( 🤷♂️
https://zhuanlan.zhihu.com/p/26660940 #JS 这个和我最近设计的一个有点像... 不过也不够简洁(20行深拷贝... 30行都能实现miniKanren了),但非常直白
#learn 那我就讲下 N/D Finite Automaton (状态机,别瞎听他们加绝对前缀,没意义),下简称 N/D
N是一个状态列表,从列表:起始/begin状态号到含完成/final 状态,一次匹配就成功。 每状态有 CharClass 对应,成功则转移到许多?状态,否则原位
abc , a[be]c , \d\w+ 都可写成字符类的状态机,^$ 蛮特殊的,它断言当前在输入首尾
N不如D适合 match ,N的所有状态是平级的,ab?c 时状态会回退a,类似 when 变 if-else 会更实际些f
a[bc]*$ 里, (S0 a (S1 [b b1-S1] [c c1-S1] ) $) b,b1 这些"连接态"是等价的,a和$ 是等价的
把 通过S0通过a 能走到(transit)的态记为 q1, q1通过b 滤出q2 ,而只要q里包含 finalCell 就是结束态 ,继续下去可把N转为D。
“ 在这里是 {n5} 。之后求它的边界, 即每一个元素都通过 ε 走到能走到的所有状态,记为 q1 的闭包。” ... 其实就是BFS/DFS
“不动点算法:算法可以提前运行终止,因为状态数是有限的” ...
while(que.size)得到 (S0 a (S1 [b [S2 bb bc] ] [c [S3 cc cb] ] ) )
q=que.pop()
for(c in q)
G[q,c] =t=dfs(links(q,c))
if(t not in Q)// Q = {q0, q1} , workList = [q1]
Q.add(t);que.add(t)
在mivik的正则库里,
NFA.from(append="a").oneOrMore 是 NFA的Builder ,StaticNFA.toDFA . CellList=StateList ;因为 outs 是 BitSet (IntSet) 还支持序列化所以很乱,但这个算法就难..在流程构造里 begin,end 是常被extend()的, link(cell, final)是状态尾部
repeat(1..2) 是最有趣的,repeatAtLeast 只是把 m.copy() extend N遍然后 m*,rep(a..b) 则是在a后放 b-a 个可跳end 的单元,遇到不在内的自然就跳出了
讲不完... 🤪 没有精力了,图形学应用还鸽着呢
正则能不能直接变DFA?
a(b|c)*$ 转NFA还好, a 喂b,c 或$ 会转移,而 cb,bc 是互相转移的最魔怔的就是直接替换为规则集的规则 a=b|c|$ ;bc=c;cb=b; 还有cb$ ,'b' 本身没喂对可以不转移,也能等价它link到的字,这是什么构造.. 1字符1转移都没会还要整理这个 🤪
知乎专栏
一起来写个简单的解释器(七):抽象语法树AST
本章,我们会推翻之前解释器的实现方法,将用“树”这种数据结构来实现解释器。为什么会用树来实现?因为算术表达式最适合用树这种数据结构来描述了。比如6/3*2+4*5,可以通过不断叠加的二叉树来表达: 这种树叫做…
Forwarded from Phonograph (Ralph 萌新喵)
https://github.com/RalXYZ/RalOS
由于 OS 课出成绩了,那么我就把我写的 OS 开源了。
它的完成度显然远远没有 Pintos 之类的高。但,作为一个单人完成、从零开始的大作业,我也只能做这么多了。
由于 OS 课出成绩了,那么我就把我写的 OS 开源了。
它的完成度显然远远没有 Pintos 之类的高。但,作为一个单人完成、从零开始的大作业,我也只能做这么多了。
GitHub
GitHub - RalXYZ/RalOS: A toy OS built from scratch, the final project of ZJU Operating System course
A toy OS built from scratch, the final project of ZJU Operating System course - GitHub - RalXYZ/RalOS: A toy OS built from scratch, the final project of ZJU Operating System course
#Rust 的 struct/impl-fn 设计非常好(好像还能作union.. ) ,也支持闭包
std 有一些 box,ref,Vec,Map 的对C++ 习惯也很直接,而且有unsafe 能取代C
值类型&mut和(i8,i8)、流控匹配, FFI 和 Emscripten 也很好 (尽管不如Kt重视语义性
总之和 #Go 这种民科语言是完全不同的,mivik/kamet,rin 都是以rs 为原型(尽管不支持匹配解构)
但是设置环境是要花时间的,如果你的程序就那样,用什么语言其实没必要。 Rust 的分配期推导越来越好了,语言工具也完善内部。基本可以直接从其他语言翻译,但是什么语言其实都一样的..
std 有一些 box,ref,Vec,Map 的对C++ 习惯也很直接,而且有unsafe 能取代C
值类型&mut和(i8,i8)、流控匹配, FFI 和 Emscripten 也很好 (尽管不如Kt重视语义性
总之和 #Go 这种民科语言是完全不同的,mivik/kamet,rin 都是以rs 为原型(尽管不支持匹配解构)
但是设置环境是要花时间的,如果你的程序就那样,用什么语言其实没必要。 Rust 的分配期推导越来越好了,语言工具也完善内部。基本可以直接从其他语言翻译,但是什么语言其实都一样的..
#js [Forwarded from 每日 AWESOME 观察]
francisrstokes / super-expressive
一个轻量级 JavaScript 库,它允许您用可读性非常高的代码构建正则表达式。让你在几个月后,仍然能读懂自己写的正则表达式。
Rime RainSlide, [2022/1/18 下午2:08]
那么,能把已有的正则表达式转换为它的函数链吗
duangsuse, [2022/1/19 下午10:22]
魔怔了, Kiot 也是这样的,但还是有正则解析
根本无关可读性, 等人学会了正则只会烦于无法重构,你多写几个试试。 又不能config ,concatRe("^" , ()=>rand()? "a":"B" ) 都强
如果一个人不懂正则.. 那他第一次是怎么写的,而且这货都不知道有没有 converter , 不能反向兼容,有整这些字串拼接的心不如写个科普文档
和 Javapoet 一样都是属于低级抽象,某知乎大佬的 ObjectRegex 或 py _re.SRE 正则对象多好,列表/嵌套文法都能处理,不会弄这种类似C宏的低意义代码。 regexp101.com 才真好呢
这是正则引擎…… 不是 regexp code 封装
呃... 现在缩进文法的语言 lexer确实应该识别 : 后的空格 ,但这是整个 lex-yacc的问题(??
脸黑君 (我會很生氣, [2022/1/19 下午10:46]
那你想重用的时候是亲自封装还是搞个这东西
duangsuse, [2022/1/19 下午10:48]
这个是实验,我是说那个 "a".repeat() = "(a)*" 的东西,还弄什么 .end() ,怕是内部维护了一个 [] push pop 吧,估计才学会这个就整框架了,还把API做成这样,没有误人子弟的嫌疑吗
如果没做 re 到他框架的转换,就只会带来麻烦(虽然类似的“正则简化” 不是第一次了
duangsuse, [2022/1/19 下午10:42]
至少这个re 可以写成
调用链应该起到强化可选配置的作用,如果没有,那应该写成
这个高大上的语法,我来个 this 上的调用归个类:
start/end ^$ 是 当前层+='^|$'
optional 不创建新层,其上 string 这些append 行为直接写入当前层[]
capture 和 exact(N) 是push(a=['()'|'{}']),等它们 end() 时按 a[0] 信息 join 一层 — capture^.exact(2).str('x').end.str(y).end 的^ end 时会拼合 x{2}和y
anyOf 看当前层是不是只有 a-b 类的单项,若是则
所以说技巧是好的,但是学会就用还需要多思考
他不会用 {noop: ``}["noop"] 吗... 果然像是JSer会做的 🌑(虽然我也是
还好吧,我觉得火不起来,要不然就说明前端只能靠 重写既有代码play 了
毕竟是涉及"二进制格式"的东西,刚才可能有点过火
francisrstokes / super-expressive
一个轻量级 JavaScript 库,它允许您用可读性非常高的代码构建正则表达式。让你在几个月后,仍然能读懂自己写的正则表达式。
Rime RainSlide, [2022/1/18 下午2:08]
那么,能把已有的正则表达式转换为它的函数链吗
duangsuse, [2022/1/19 下午10:22]
魔怔了, Kiot 也是这样的,但还是有正则解析
根本无关可读性, 等人学会了正则只会烦于无法重构,你多写几个试试。 又不能config ,concatRe("^" , ()=>rand()? "a":"B" ) 都强
如果一个人不懂正则.. 那他第一次是怎么写的,而且这货都不知道有没有 converter , 不能反向兼容,有整这些字串拼接的心不如写个科普文档
和 Javapoet 一样都是属于低级抽象,某知乎大佬的 ObjectRegex 或 py _re.SRE 正则对象多好,列表/嵌套文法都能处理,不会弄这种类似C宏的低意义代码。 regexp101.com 才真好呢
这是正则引擎…… 不是 regexp code 封装
呃... 现在缩进文法的语言 lexer确实应该识别 : 后的空格 ,但这是整个 lex-yacc的问题(??
脸黑君 (我會很生氣, [2022/1/19 下午10:46]
那你想重用的时候是亲自封装还是搞个这东西
duangsuse, [2022/1/19 下午10:48]
这个是实验,我是说那个 "a".repeat() = "(a)*" 的东西,还弄什么 .end() ,怕是内部维护了一个 [] push pop 吧,估计才学会这个就整框架了,还把API做成这样,没有误人子弟的嫌疑吗
如果没做 re 到他框架的转换,就只会带来麻烦(虽然类似的“正则简化” 不是第一次了
duangsuse, [2022/1/19 下午10:42]
至少这个re 可以写成
Re().inpStart.maybe('0x').capture(repeat("A-Fa-f0-9",4) ).inpEnd.ok 这样的形式,调用链也别整太长了调用链应该起到强化可选配置的作用,如果没有,那应该写成
User().alsoIt{name="xx";age=9} 和 {clas:"main", } 的直接形式,滥用不是很好这个高大上的语法,我来个 this 上的调用归个类:
start/end ^$ 是 当前层+='^|$'
optional 不创建新层,其上 string 这些append 行为直接写入当前层[]
capture 和 exact(N) 是push(a=['()'|'{}']),等它们 end() 时按 a[0] 信息 join 一层 — capture^.exact(2).str('x').end.str(y).end 的^ end 时会拼合 x{2}和y
anyOf 看当前层是不是只有 a-b 类的单项,若是则
[a-b] 否则 (a|b)所以说技巧是好的,但是学会就用还需要多思考
他不会用 {noop: ``}["noop"] 吗... 果然像是JSer会做的 🌑(虽然我也是
还好吧,我觉得火不起来,要不然就说明前端只能靠 重写既有代码play 了
毕竟是涉及"二进制格式"的东西,刚才可能有点过火
GitHub
GitHub - francisrstokes/super-expressive: 🦜 Super Expressive is a zero-dependency JavaScript library for building regular expressions…
🦜 Super Expressive is a zero-dependency JavaScript library for building regular expressions in (almost) natural language - francisrstokes/super-expressive
http://www.yinwang.org/blog-cn/2015/04/03/paradigms
“JS 没法访问外层的 this,非得“bind”一下。Python 的变量定义和赋值不分,所以你需要访问全局变量的时候得用 global 关键字,后来又发现如果要访问“中间层”的变量,没有办法了,所以又加了个 nonlocal 关键字。Ruby 先后出现过四种类似 lambda 的东西,每个都有自己的怪癖…… 有些人问我为什么有些语言设计成那个样子,我只能说,很多语言设计者其实根本不知道自己在干什么。
🤔其实这是半对半错的。var self=this 问题可以用 ()=> 解决了,Python3 的 nonlocal 也不是没意义的,比 Java 的
尽管它们都不如 Kotlin 。你看王垠批判Go,质疑 Rust ,但他敢直接怼Kt 的语言设计吗?只好捡了没
http://www.yinwang.org/blog-cn/2013/04/01/lazy-evaluation
Haskell 的参数惰性计算确实需要一个0参闭包,而且是运行时的,较难优化 🤔 所以我觉得如果用指针set 去init 一个变量比较好,然而实际上
Unification(值/变量归一求解) 能用于简单类型系统,且不能推导 subtype 交集,他的逻辑性质名词(symm对称性)也是对的
代数数据类型(带类型参的 Sumtype brach) data A t { Lit :: Int->A Int ; Obj :: t->A t } 其实在 subtype+typeparam 里也能做吧
后来我还在这里看到 recursive type 是指
我以前都把 Bool*Bool=4 state, B+B=2 当冷知识看,没想到真有人把它当东西,置顶一个计科专业还说类型 +* ,还有 0=Void 1=() .. 于是 0*1=0, 0+1=1 ?还不如 insect/union 直白呢. [绝对报错/无尽]计算&T=Nothing, 它|T=T ,因为能算到的Nothing?就肯定不是throw,exit()等,所以意义? 我跟数学老师说 Sigma_i=0^6 表示不如 (0..6).sumBy{} ,老师说那不过是个形式,无关意义。
乱ref英文名词和小众概念,老实说我现在是完全不吃这一套,要么你给我讲明白、指出我的误解,要么拿代码和解决的问题来, 抽象代数x编程,只当耳边风;要是具体一点还夸你厉害,毕竟大家都没空了解这些碎片,我怎么知道你有逻辑自恰性,或者只是碰巧通过检查的数学摘抄?
不过缩进文法(layout) 是香的..也没严重影响解析器的复杂性
http://www.yinwang.org/blog-cn/2014/04/18/golang
这个点评就写得不错,尤其是 TSorter{Swap,Len, Less} 真的比java.Comparator弱智了。
妈的, callback 都能叫 CPS(不返回编程/面向程续编程),这么一想也是噢,调用能决定之后的取舍,怎么不是CPS形式.. Kt协程就是把 suspend fun 加个 callback 交互,因为它没有 TS 的 __awaiter 也不便用闭包this做Generator吧. 再通过StateMachine yield恢复 #kotlin
这里就体现了王垠这人是有真才实学的, 他的知识集不比 #zhihu 一众(公知都算不上,因为从不做科普)的PLT人差
很可惜后来越来越极端,而且也不乐意分享他的compiler.ss外一些其他成果;但就讲课&讲故事而言,
我觉得知乎的大家都没资格评价这个人,因为你们在这点甚至不如他.. 他的博客确实没逼格,话也没轻没重,但对CS科普的贡献是巨大的,真的不夸张。
大家都喜欢轻视「娱乐编程界」,但仔细想想,我们何尝不是娱乐编程人士? 你的回答里贴了多少思路和代码,有多为提问者和公众程序员着想?
如果是为自娱自乐,不算在另一种娱乐编程吗?
ps. 我是不认为太自我的「个人博文」能算科普的。科普必须对不同做法有一定了解,有穿插和客观解读比对,告诉大家好坏要点,而不是代码的解说。不然本质上和"Java入门"教程也没区别
“JS 没法访问外层的 this,非得“bind”一下。Python 的变量定义和赋值不分,所以你需要访问全局变量的时候得用 global 关键字,后来又发现如果要访问“中间层”的变量,没有办法了,所以又加了个 nonlocal 关键字。Ruby 先后出现过四种类似 lambda 的东西,每个都有自己的怪癖…… 有些人问我为什么有些语言设计成那个样子,我只能说,很多语言设计者其实根本不知道自己在干什么。
🤔其实这是半对半错的。var self=this 问题可以用 ()=> 解决了,Python3 的 nonlocal 也不是没意义的,比 Java 的
int a;new T(){a=1;} Error: effective final 好,因为闭包一般不会mut变量,乃至 global ;然后 Ruby 的 ->(){} 和 do|| , Proc.new(&f) 确实是有严重问题,这方面它不如 Python , Matz 对技巧太贪心了。像 C# 尽管技巧多也不会出现函数值有几种写法的问题,但一入 Ruby 你就要学会 1.yield_self{|x| } 和 do|x| end 这些... 坦白说不值,但Rb是有历史包袱.尽管它们都不如 Kotlin 。你看王垠批判Go,质疑 Rust ,但他敢直接怼Kt 的语言设计吗?只好捡了没
throws Exception 强制检查的问题(然而 runCatching{}.getOrNull 用的香谁会管他呢http://www.yinwang.org/blog-cn/2013/04/01/lazy-evaluation
Haskell 的参数惰性计算确实需要一个0参闭包,而且是运行时的,较难优化 🤔 所以我觉得如果用指针set 去init 一个变量比较好,然而实际上
if(!init)x=initizr(); 里这个if确实要执行成千上万次,除非动态改汇编。也不是编译能优化的”解释器开销“Unification(值/变量归一求解) 能用于简单类型系统,且不能推导 subtype 交集,他的逻辑性质名词(symm对称性)也是对的
代数数据类型(带类型参的 Sumtype brach) data A t { Lit :: Int->A Int ; Obj :: t->A t } 其实在 subtype+typeparam 里也能做吧
后来我还在这里看到 recursive type 是指
type R= T0+ Int*R (常规意义: +=| *=,),于是 T0*Int*Int 都是这种.. 就是编译期允许递归 Type.check(inst:Type) 吧.. 算了,唉,我只是讨厌没有编译期计算我以前都把 Bool*Bool=4 state, B+B=2 当冷知识看,没想到真有人把它当东西,置顶一个计科专业还说类型 +* ,还有 0=Void 1=() .. 于是 0*1=0, 0+1=1 ?还不如 insect/union 直白呢. [绝对报错/无尽]计算&T=Nothing, 它|T=T ,因为能算到的Nothing?就肯定不是throw,exit()等,所以意义? 我跟数学老师说 Sigma_i=0^6 表示不如 (0..6).sumBy{} ,老师说那不过是个形式,无关意义。
乱ref英文名词和小众概念,老实说我现在是完全不吃这一套,要么你给我讲明白、指出我的误解,要么拿代码和解决的问题来, 抽象代数x编程,只当耳边风;要是具体一点还夸你厉害,毕竟大家都没空了解这些碎片,我怎么知道你有逻辑自恰性,或者只是碰巧通过检查的数学摘抄?
不过缩进文法(layout) 是香的..也没严重影响解析器的复杂性
http://www.yinwang.org/blog-cn/2014/04/18/golang
这个点评就写得不错,尤其是 TSorter{Swap,Len, Less} 真的比java.Comparator弱智了。
妈的, callback 都能叫 CPS(不返回编程/面向程续编程),这么一想也是噢,调用能决定之后的取舍,怎么不是CPS形式.. Kt协程就是把 suspend fun 加个 callback 交互,因为它没有 TS 的 __awaiter 也不便用闭包this做Generator吧. 再通过StateMachine yield恢复 #kotlin
这里就体现了王垠这人是有真才实学的, 他的知识集不比 #zhihu 一众(公知都算不上,因为从不做科普)的PLT人差
很可惜后来越来越极端,而且也不乐意分享他的compiler.ss外一些其他成果;但就讲课&讲故事而言,
我觉得知乎的大家都没资格评价这个人,因为你们在这点甚至不如他.. 他的博客确实没逼格,话也没轻没重,但对CS科普的贡献是巨大的,真的不夸张。
大家都喜欢轻视「娱乐编程界」,但仔细想想,我们何尝不是娱乐编程人士? 你的回答里贴了多少思路和代码,有多为提问者和公众程序员着想?
如果是为自娱自乐,不算在另一种娱乐编程吗?
ps. 我是不认为太自我的「个人博文」能算科普的。科普必须对不同做法有一定了解,有穿插和客观解读比对,告诉大家好坏要点,而不是代码的解说。不然本质上和"Java入门"教程也没区别
https://zhuanlan.zhihu.com/p/34064655 #zhihu #fp 这是个物理爱好者18年的 CPS 解释器实现文
这个代码质量.. 其实我是讨厌看lisp 系的,非常讨厌嵌套括号,而且这个人似乎在代码里插debug print ,以C的方法写Racket .. ,那我就把要点挑出来看看
函数式除了伪递归也可做尾调用优化,当 f=()=>g(), g=()=>1 时g可return 到f.retAddr ,类似函数内联。CPS也是尾调用
显然return/即 callstk.pop() 两次不叫优化,那么我们就需要 pop 外的方式使用调用栈,即保存 continuation程续 。(call/cc setAs-f) 抓住了调用后的步骤, (f) 就能跳回原位(可用ES6 func* 体验);在工业语言里程续被用于断续执行一个函数,意味它可yield暂时回交执行权, call/cc 也一样,不过能替换调用栈。 这样调用栈就变调用图了,因为你可 longjmp() 到之前的栈帧..
call/cc 可在 generator 里实现yield ,乃至throwcc-恢复 (不过认为Lisp throw更高科技真的不现实。
想实现程续可不容易,因为解释器的执行是不可暂停的,而我们还要替换调用栈 ,于是要手写新的..
cps 解释器可以实现 callcc 。考虑 "1".eval() 时要等待、更大的 "1+2.." 要等待,若碰到 "(call/cc f)" 也等待,f 就拿不到 callcc f后的程续,因为callcc 是立即执行f的?
他是用了 handle-decision-tree , 一个 (eval exp env cont) 扩展,
eval-continuation-call 简单让 eval 的 cont=被闭包状态 ,因为[接下来的步骤 回调] cont 本身就是解释器状态
CPS化 eval() 就能保存状态..我之前只想过 func* 化,确实对 #PLT 了解不够。 比如 a+1 里 add: lit(v=> a,b=v) , cont(a+b) ,那如果 lit 把 add() 闭包保留住,确实就留住了当前程续,如果env 参数也在内,就是整个状态副本…… 不过没看懂单步操作为啥需 decision-tree (好像说 eval-fcall 的没这个就不能在 callcc 函数内运行
文末说要点是 callcc f 创建了 continuation 喂给 f,我只看到 cps 使得后序树遍历 Expr>Add>Lit 变成 Lit>Add>Expr 的反向形式,因此可回馈值 而非等待值,就能suspend ;我之前设计的 tvr 求值步骤模型里 r(treeRes) 好像也是这个效果,能保留树路径。从自顶向下组合 到自底向上化简,碰到callcc抓callback..我好像明白下面那个GLL 是什么意思了
eval-conti-call 则把包住的 env ,cont 重设,就替换了调用栈和当前 scope
如果 f=(k)=>的程续k没被恢复,callcc f 的值就是f()值。 callcc的eval(里cont= "call f" ) ,就指定此默认值
不过说起来函数式解释器 env 表全复制也挺高明的,就没有什么继承综合语法的破事了,解析期都构造好的深先序
以前那些人都说得太复杂…… 之前还有个CPS写解析组合子的,能实现memo..用了什么CPS特性? tramp(循环转化)和memo? GLL文法? 代码太长.. 就没发现CPS有啥特殊技巧的说
🥲我想起了去年冰封说”Kotlin coroutine 的 Context 就是CPS Context“ (
函数式妙……实在是妙啊, callback 这么的土,Kt 竟然用它实现协程?它竟是CPS? 真的大跌眼镜。看来相同的东西,动手操作不同,名词也就不同了
解释器,我自己也在写一篇。
http://www.yinwang.org/blog-cn/2012/08/01/interpreter
王某这个racket 再定解释器就是S-expr 系非常经典的写法了, 变量/常量和env, lambda/call, (eval expr env) ,但其实要说短也有更短的:好像删了..
😐坏耶,只是个后序遍历还藏那么深,殊不知Lisp系解释器作用域是代码最低的... 改天我写一个
看到这里,其实这些人除了S-表达式看起来很牛逼,代码质量真的不行;不是我讨厌圆括号,而是
许多人尽管是大佬,但写的代码仍不敢恭维,或者有改进空间;而因为他的思想正确、领域牛逼,言辞有特性,就觉得他什么都好, 我大可不必。错就是错,风格是慢慢改进的。 尽管牛逼,如果言不及义,也只能说他不善表达..意识到才有改进空间
除了PLT,我也在发展一些其他的API见闻,在我眼里什么领域都是平等的,表达只有目的和废话,没有谁更牛逼的说法。
这个代码质量.. 其实我是讨厌看lisp 系的,非常讨厌嵌套括号,而且这个人似乎在代码里插debug print ,以C的方法写Racket .. ,那我就把要点挑出来看看
函数式除了伪递归也可做尾调用优化,当 f=()=>g(), g=()=>1 时g可return 到f.retAddr ,类似函数内联。CPS也是尾调用
显然return/即 callstk.pop() 两次不叫优化,那么我们就需要 pop 外的方式使用调用栈,即保存 continuation程续 。(call/cc setAs-f) 抓住了调用后的步骤, (f) 就能跳回原位(可用ES6 func* 体验);在工业语言里程续被用于断续执行一个函数,意味它可yield暂时回交执行权, call/cc 也一样,不过能替换调用栈。 这样调用栈就变调用图了,因为你可 longjmp() 到之前的栈帧..
call/cc 可在 generator 里实现yield ,乃至throwcc-恢复 (不过认为Lisp throw更高科技真的不现实。
想实现程续可不容易,因为解释器的执行是不可暂停的,而我们还要替换调用栈 ,于是要手写新的..
cps 解释器可以实现 callcc 。考虑 "1".eval() 时要等待、更大的 "1+2.." 要等待,若碰到 "(call/cc f)" 也等待,f 就拿不到 callcc f后的程续,因为callcc 是立即执行f的?
他是用了 handle-decision-tree , 一个 (eval exp env cont) 扩展,
x[0](res=>res? only(decision=x[1])? exec(d=decision) : decide(d) : decide(tail)) eval-continuation-call 简单让 eval 的 cont=被闭包状态 ,因为[接下来的步骤 回调] cont 本身就是解释器状态
CPS化 eval() 就能保存状态..我之前只想过 func* 化,确实对 #PLT 了解不够。 比如 a+1 里 add: lit(v=> a,b=v) , cont(a+b) ,那如果 lit 把 add() 闭包保留住,确实就留住了当前程续,如果env 参数也在内,就是整个状态副本…… 不过没看懂单步操作为啥需 decision-tree (好像说 eval-fcall 的没这个就不能在 callcc 函数内运行
文末说要点是 callcc f 创建了 continuation 喂给 f,我只看到 cps 使得后序树遍历 Expr>Add>Lit 变成 Lit>Add>Expr 的反向形式,因此可回馈值 而非等待值,就能suspend ;我之前设计的 tvr 求值步骤模型里 r(treeRes) 好像也是这个效果,能保留树路径。从自顶向下组合 到自底向上化简,碰到callcc抓callback..我好像明白下面那个GLL 是什么意思了
eval-conti-call 则把包住的 env ,cont 重设,就替换了调用栈和当前 scope
如果 f=(k)=>的程续k没被恢复,callcc f 的值就是f()值。 callcc的eval(里cont= "call f" ) ,就指定此默认值
不过说起来函数式解释器 env 表全复制也挺高明的,就没有什么继承综合语法的破事了,解析期都构造好的深先序
以前那些人都说得太复杂…… 之前还有个CPS写解析组合子的,能实现memo..用了什么CPS特性? tramp(循环转化)和memo? GLL文法? 代码太长.. 就没发现CPS有啥特殊技巧的说
🥲我想起了去年冰封说”Kotlin coroutine 的 Context 就是CPS Context“ (
runBlocking{ launch{ delay(1000) } } 两个suspend调用里啥算内部协程,还是两个孤立调度的),然后我一脸蒙蔽- 断续函数就是可暂断回调度器的函数,和CPS有啥关系? ES6 里也只和 __awaiter 自动.then(resume) 有关啊,现在才知道 Kt coro 是自动callback:Continuation{suspend,resume} 化suspend fun,内部调用等待,完成后交给最外层...函数式妙……实在是妙啊, callback 这么的土,Kt 竟然用它实现协程?它竟是CPS? 真的大跌眼镜。看来相同的东西,动手操作不同,名词也就不同了
解释器,我自己也在写一篇。
http://www.yinwang.org/blog-cn/2012/08/01/interpreter
王某这个racket 再定解释器就是S-expr 系非常经典的写法了, 变量/常量和env, lambda/call, (eval expr env) ,但其实要说短也有更短的:好像删了..
😐坏耶,只是个后序遍历还藏那么深,殊不知Lisp系解释器作用域是代码最低的... 改天我写一个
看到这里,其实这些人除了S-表达式看起来很牛逼,代码质量真的不行;不是我讨厌圆括号,而是
(def prechk(x) (if (not (ok x))(err "fxxk") 正常逻辑 ) ) 还占缩进这种写法真的难看,我最讨厌平铺直叙的程序 🙏 许多人尽管是大佬,但写的代码仍不敢恭维,或者有改进空间;而因为他的思想正确、领域牛逼,言辞有特性,就觉得他什么都好, 我大可不必。错就是错,风格是慢慢改进的。 尽管牛逼,如果言不及义,也只能说他不善表达..意识到才有改进空间
除了PLT,我也在发展一些其他的API见闻,在我眼里什么领域都是平等的,表达只有目的和废话,没有谁更牛逼的说法。
知乎专栏
柯里化的前生今世(八):尾调用与CPS
关于本文是系列文章中的第八篇,发布在 业余程序员的个人修养这个专栏中: 柯里化的前生今世(一):函数面面观 柯里化的前生今世(二):括号神教 柯里化的前生今世(三):语言和同像性 柯里化的前生今世(四)…