/tmp/duangsuse.sock
作为一个附属的内容,接下来我说一下作局部用域的问题。 #PLT #Parser 我自己是没时间实现更多的东西了... 大家了解这些理论呢... 可以回去尝试一下。 首先咱们谈谈 Lexical Scoping. 介个 lexical scoping,词法作用域呢,是 1932 年 Lambda calculus 引入的一个(迫真)概念 Lambda calculus is a formal system in mathematical logic for expressing computation…
实际上是一个『覆盖』,但是这种情况的话,只要有名称冲突(当然使用真正意义上的 Symbol 的除外)就炸了
正确的方法是,记录每一个 abstraction 的所有 variable binding,只要发现和自己正在填充的项目有冲突,就自觉在离开这个 abstraction 的 body 之前不要填充同名项目(维护一个 stack<set>),等待到时候再填
3. 你使用了副作用,类似 Lua 的实现
就是 1. 你实现了完整的 dynamic scoping(准确的说只是 local-scoping) 2. 你的实现还对 lexical scoping 依赖的词法闭包有支持
Lua 里一切作用域都是函数作用域(全局的也是全局函数作用域...)
一大堆 upvalue... 每个 UpValue 在 GC 层面有 open 和 close 两个状态,如果 closed,则代表 UpValue 集合所在的函数实例已经结束运行了,如果没有引用可以 free 掉
Lua 里就是这样:在 one-pass(一遍完成 tokenize词法分析/parse句法分析/codegen代码生成 )解析的时候,就已经处理好了当前函数的 upvalue 问题(Lua 里只有闭包没有单纯的函数,闭包状态的被称为 FuncState),因为它会对每个符号寻找引用 — 是不是本地变量?(参数) 是不是 UpValue? 否则就是全局变量。(Lua 官方实现的
对于每个外部函数(outter function),你都做好了返回闭包的准备,而且闭包都是封闭的 FuncState 局部状态存储,引用了外层函数的 UpValue
这样自己要新建本地变量(参数) — 直接新建本地表即可
然后要找外层的引用 — 到 UpValue 里面找
交回执行权 — 删掉本地表,UpValue 的引用计数减去 1
UpValue 的创建也可以是即时的,不过不能缓存复用计算结果就很淡疼,到底还是免去了复制的开销...(只是保留了 FuncState,GC 压力还是免不了)
当然也可以不修改拿本地引用的代码,在覆盖/新增 UpValue 的时候惰性创建 Map/Set 记录修改,栈弹出本层执行实例的时候撤销对父级的修改即可。
正确的方法是,记录每一个 abstraction 的所有 variable binding,只要发现和自己正在填充的项目有冲突,就自觉在离开这个 abstraction 的 body 之前不要填充同名项目(维护一个 stack<set>),等待到时候再填
\x -> (\x -> x + 1) x填充完成 body 之后,我们就得到了 body [ x := 1, y := 2, ...] 什么的,可以直接拿去再次折叠求值了(递归下去,到了 primitive operator 那里就可以直接求值,否则再次应用函数,再次 substitute... 最终最多要遍历 n+n1+n2+... 次)
equiv \y -> (\x -> x + 1) y
3. 你使用了副作用,类似 Lua 的实现
就是 1. 你实现了完整的 dynamic scoping(准确的说只是 local-scoping) 2. 你的实现还对 lexical scoping 依赖的词法闭包有支持
Lua 里一切作用域都是函数作用域(全局的也是全局函数作用域...)
一大堆 upvalue... 每个 UpValue 在 GC 层面有 open 和 close 两个状态,如果 closed,则代表 UpValue 集合所在的函数实例已经结束运行了,如果没有引用可以 free 掉
Lua 里就是这样:在 one-pass(一遍完成 tokenize词法分析/parse句法分析/codegen代码生成 )解析的时候,就已经处理好了当前函数的 upvalue 问题(Lua 里只有闭包没有单纯的函数,闭包状态的被称为 FuncState),因为它会对每个符号寻找引用 — 是不是本地变量?(参数) 是不是 UpValue? 否则就是全局变量。(Lua 官方实现的
singlevar 子程序)对于每个外部函数(outter function),你都做好了返回闭包的准备,而且闭包都是封闭的 FuncState 局部状态存储,引用了外层函数的 UpValue
这样自己要新建本地变量(参数) — 直接新建本地表即可
然后要找外层的引用 — 到 UpValue 里面找
交回执行权 — 删掉本地表,UpValue 的引用计数减去 1
UpValue 的创建也可以是即时的,不过不能缓存复用计算结果就很淡疼,到底还是免去了复制的开销...(只是保留了 FuncState,GC 压力还是免不了)
当然也可以不修改拿本地引用的代码,在覆盖/新增 UpValue 的时候惰性创建 Map/Set 记录修改,栈弹出本层执行实例的时候撤销对父级的修改即可。
/tmp/duangsuse.sock
实际上是一个『覆盖』,但是这种情况的话,只要有名称冲突(当然使用真正意义上的 Symbol 的除外)就炸了 正确的方法是,记录每一个 abstraction 的所有 variable binding,只要发现和自己正在填充的项目有冲突,就自觉在离开这个 abstraction 的 body 之前不要填充同名项目(维护一个 stack<set>),等待到时候再填 \x -> (\x -> x + 1) x equiv \y -> (\x -> x + 1) y 填充完成 body 之后,我们就得到了 body…
那么这就是 lexical scoping 的三点,下面是(冰封哥讲过的,我也不说多了)dynamic scoping 的三点
dynamic scoping 有一个不符合直觉的点 — 就是它是看你函数实例嵌套的情况解析符号引用的
Global { g: 1 }
(stack) [
[current]
{ w: 'var1' },
{ v: 'var0' },
] (stack bottom)
你在当前层找本地变量/参数的名字,如果找到则矣,找不到就到父层找... 父层的父层找... (有点类似基于虚表不过没虚表缓存... 面向对象的继承,轻量级的 JavaScript 原型链...)
在不使用高阶函数的时候是看不出区别的(因为一般函数嵌套就是那个样子,和局部作用域对称),
不过这就有一个糟心的,就是产生覆盖的时候,和你要创建闭包的时候 —
(当然这个是没有使用独立具有唯一标识性的 Symbol 导致的,但还有一个没法解决的)
因为 function (k) k 实际上运行时在『动态』寻找对 free variable k 的 binding,然而呢,找不到
你以为引用的是 UpValue 'k',实际上 konst 的 k 当时没有被求值(因为是动态求值的),匿名函数要找的是作用域栈上最近定义叫 'k' 的变量定义.... 没有就挂了,最可怕的是『有』 — 这样的话你就完全不知道程序是要做什么了....
当然这个如此 trivial 的内容王垠也讲过,所以我不多说了,怎么实现它吧。
一般来说最直接的思路是在调用栈\与之对称的栈上面加上局部作用域表,就像上面的那个 stack 一样
这样两个选择 1. 每一层的所有 K-V 对都从父层作用域复制 2. 每次建立空表或者只包含参数定义的表,需要时再去找,找不到就到父层找、父层的父层找... 当然也可以找到了就在本层缓存下来,不过这有个性能问题。
冰封哥的中文博客上几年前也提到过,就是这个 小 Lice 语言 一般不需要多少参数,
首先就是每层实例建立个『作用域表』,太浪费了,毕竟是 HashMap 啊,为时间优化的数据,建立多了内存分配开销不起
然后几年前的 冰封哥就这么想:
1. 为什么不把这些东西的开销放在『编译期』,先做成 int id 的形式,然后每次去用 id 找本地变量数组/父层的本地变量,不就快狠多(实际上 Ruby 1.9 时期的 YARV 就是这么实现的,有个
2. 或许可以做成
最后冰封哥想到了编译原理教程『龙书』(是 《编译原理、技术和工具》)也提供的一种优化方案 — 在进入作用域前先把这个可能的冲突记录好(比如某某某之前是 undefined、某某某之前是 1 现在被改成了 2),离开作用域就撤销修改(如果没有引入新变量也没覆盖变量就不存在额外开销,而且这个『记录表』还可以建立轻量点,比如可以只是二分查找 Map)
这样全局就一个『作用域动态解析表』了,但却可以有很多作用域。
https://ice1000.org/2017/03/10/DifferenceBetweenMeAndDragonBook/
是两年前的事情了(
当然我快一年前看完后也有点感想,就是为什么一定要把这个『保留备份』的时序放在开始求值 body 之前,为什么不能在需要赋值发生冲突 / 建立新变量键 的时候动态维护这个表,这样就不一定需要一个 let/application 了
当然这也是一种方法,就是 VersionizedMap 喽,有版本控制的 Map... 每次冲突的时候记录、需要的时候回滚
此外单单基于 JVM 也可以把这个 Map 的 Key 做成 『可能是 Any 也可能是 Stack』 的情况,如果是 某种 Stack 就代表变量名称存在冲突,需要选择某个作用域特定的版本
不过,我们都没有想到的是,分配一个唯一标识符『名称』想不冲突在存储程序型计算机甚至世界里都是简单而且低开销的... 我们都不知道,其实全局本来有一个 Map 就足够了,覆盖的情况其实不需要考虑,因为本来就不该是由程序员去考虑这个『变量表』『名称』覆盖问题的....
那么这次的两作用域就说到这里
感兴趣的同学可以再找一下 传参模型 — 传值(by value)、惰性(by name)、表达式(by expr)
和『值』、『类型』方面的问题,比如 Sum type、Product type、value reference/pointer 和 by copy/by ref
dynamic scoping 有一个不符合直觉的点 — 就是它是看你函数实例嵌套的情况解析符号引用的
Global { g: 1 }
(stack) [
[current]
{ w: 'var1' },
{ v: 'var0' },
] (stack bottom)
你在当前层找本地变量/参数的名字,如果找到则矣,找不到就到父层找... 父层的父层找... (有点类似基于虚表不过没虚表缓存... 面向对象的继承,轻量级的 JavaScript 原型链...)
在不使用高阶函数的时候是看不出区别的(因为一般函数嵌套就是那个样子,和局部作用域对称),
不过这就有一个糟心的,就是产生覆盖的时候,和你要创建闭包的时候 —
(define g "global")看起来明明我们完全不知道 get-var 还有一个叫 g 的参数(本地变量),可它偏偏就冲突了(本来我们希望是 global 的 g,可是由于查找算法的莫名其妙,导致我们找到了『最近被定义』的 g),导致了这种莫名其妙的行为
(defun get-var g)
(display (get-var)) ; "global"
(defun get-var-here (g) (get-var))
(get-var-here "overriden") ; "overriden"
(当然这个是没有使用独立具有唯一标识性的 Symbol 导致的,但还有一个没法解决的)
(defun konst (k) (function () k)) (display ((konst 1))) ; undefined看起来不是非常正常,可是实际上运行时,k 就变成了
undefined...因为 function (k) k 实际上运行时在『动态』寻找对 free variable k 的 binding,然而呢,找不到
你以为引用的是 UpValue 'k',实际上 konst 的 k 当时没有被求值(因为是动态求值的),匿名函数要找的是作用域栈上最近定义叫 'k' 的变量定义.... 没有就挂了,最可怕的是『有』 — 这样的话你就完全不知道程序是要做什么了....
当然这个如此 trivial 的内容王垠也讲过,所以我不多说了,怎么实现它吧。
一般来说最直接的思路是在调用栈\与之对称的栈上面加上局部作用域表,就像上面的那个 stack 一样
这样两个选择 1. 每一层的所有 K-V 对都从父层作用域复制 2. 每次建立空表或者只包含参数定义的表,需要时再去找,找不到就到父层找、父层的父层找... 当然也可以找到了就在本层缓存下来,不过这有个性能问题。
冰封哥的中文博客上几年前也提到过,就是这个 小 Lice 语言 一般不需要多少参数,
首先就是每层实例建立个『作用域表』,太浪费了,毕竟是 HashMap 啊,为时间优化的数据,建立多了内存分配开销不起
然后几年前的 冰封哥就这么想:
1. 为什么不把这些东西的开销放在『编译期』,先做成 int id 的形式,然后每次去用 id 找本地变量数组/父层的本地变量,不就快狠多(实际上 Ruby 1.9 时期的 YARV 就是这么实现的,有个
(level, slot) )2. 或许可以做成
Map<String, Vector<Any>> ,代表『同名』变量在不同作用域有的不同值,可是 Vector 的开销也不小... 何况还是 synchronized 的;此外冰封的一位朋友也这么想最后冰封哥想到了编译原理教程『龙书』(是 《编译原理、技术和工具》)也提供的一种优化方案 — 在进入作用域前先把这个可能的冲突记录好(比如某某某之前是 undefined、某某某之前是 1 现在被改成了 2),离开作用域就撤销修改(如果没有引入新变量也没覆盖变量就不存在额外开销,而且这个『记录表』还可以建立轻量点,比如可以只是二分查找 Map)
这样全局就一个『作用域动态解析表』了,但却可以有很多作用域。
https://ice1000.org/2017/03/10/DifferenceBetweenMeAndDragonBook/
是两年前的事情了(
当然我快一年前看完后也有点感想,就是为什么一定要把这个『保留备份』的时序放在开始求值 body 之前,为什么不能在需要赋值发生冲突 / 建立新变量键 的时候动态维护这个表,这样就不一定需要一个 let/application 了
当然这也是一种方法,就是 VersionizedMap 喽,有版本控制的 Map... 每次冲突的时候记录、需要的时候回滚
此外单单基于 JVM 也可以把这个 Map 的 Key 做成 『可能是 Any 也可能是 Stack』 的情况,如果是 某种 Stack 就代表变量名称存在冲突,需要选择某个作用域特定的版本
不过,我们都没有想到的是,分配一个唯一标识符『名称』想不冲突在存储程序型计算机甚至世界里都是简单而且低开销的... 我们都不知道,其实全局本来有一个 Map 就足够了,覆盖的情况其实不需要考虑,因为本来就不该是由程序员去考虑这个『变量表』『名称』覆盖问题的....
那么这次的两作用域就说到这里
感兴趣的同学可以再找一下 传参模型 — 传值(by value)、惰性(by name)、表达式(by expr)
和『值』、『类型』方面的问题,比如 Sum type、Product type、value reference/pointer 和 by copy/by ref
Forwarded from Deleted Account
Telegram 什么时候支持 strike line 了,我还在用 unicode combination strikeout
说实话,我对之前表现的那么差,学习成绩也不怎么样的自己 能够有这些进步 也很满意了
我希望这些进步的确是 已经不止在计算机 的能力上出现了 而且还扩展到了更远的地方
我希望这些进步的确是 已经不止在计算机 的能力上出现了 而且还扩展到了更远的地方
不得不说,总是要学会学习的... 其实就是从你 不注意的『前端』方面,也会看到不少你觉得很巧妙的解决方式
你之前都不知道,或者存在误解\想的太简单(比如因为我不熟悉图形混成,把 shape 的 Background 当成了一定是背景色、不能是『底下』的 shape... 比如我不知道重复的 shape 可以使用很多 shape 组合成,而且还可以让混成器切掉一些叠合图)
虽然之前的确隐约有这种感觉(迫真自我安慰)
果然实践最重要
你之前都不知道,或者存在误解\想的太简单(比如因为我不熟悉图形混成,把 shape 的 Background 当成了一定是背景色、不能是『底下』的 shape... 比如我不知道重复的 shape 可以使用很多 shape 组合成,而且还可以让混成器切掉一些叠合图)
虽然之前的确隐约有这种感觉
如果有喜欢前端的同学,我推荐 《Qt5.9 C++ 开发指南》那本,对(基于 Qt5 Paint 的)图形绘制和混成讲的很清楚。
不过我没有多看,而且说句题外话,我算法也不太好(不会动态规划,dfs bfs 勉强会一点点)
不过我没有多看,而且说句题外话,我算法也不太好(不会动态规划,dfs bfs 勉强会一点点)
/tmp/duangsuse.sock
利用惰性导出和代理,来实现递归(没 Y 组合子的好,迫真,虽然 Y 组合子是递归自己...)
FP.js 是第一个我手写解析组合子的设计/实现
它还存在很多不足,比如错误信息的嵌套本来不应该传递字符串,以更好地表述解析错误的信息
比如 Feeder 架构冗余,而且没有实现本来绝对可以实现的随机 mark/reset
比如语法错误在 possible 解析器里处理的不好,会使得一些错误 unsound 并且导致报错信息莫名其妙;当使用 new Error workaround 的时候更是完全不可取,它还会破坏掉 runParser 的 Either 封装。
未来,如果还有机会,我会用 Java 8 重新实现一个 PEG 解析组合子,新组合子的错误处理将基于嵌套异常捕获计数(这样就可以在不依赖专门语言工具的情况下做到尽可能小的运行时开销)、解决 possible 的『盲目重试』问题(并且也将提供 更多类型的异常,SoundPfail)(补充:其实 def/let/just 的问题只是我个人对解析组合设计的不好而已,这是在讲以后框架上可能的一个改进)。并且将会可能支持一些解析器组合时的优化/动态分支重排序/字符串扫描 SIMD 优化什么的
它还存在很多不足,比如错误信息的嵌套本来不应该传递字符串,以更好地表述解析错误的信息
比如 Feeder 架构冗余,而且没有实现本来绝对可以实现的随机 mark/reset
比如语法错误在 possible 解析器里处理的不好,会使得一些错误 unsound 并且导致报错信息莫名其妙;当使用 new Error workaround 的时候更是完全不可取,它还会破坏掉 runParser 的 Either 封装。
未来,如果还有机会,我会用 Java 8 重新实现一个 PEG 解析组合子,新组合子的错误处理将基于嵌套异常捕获计数(这样就可以在不依赖专门语言工具的情况下做到尽可能小的运行时开销)、解决 possible 的『盲目重试』问题(并且也将提供 更多类型的异常,SoundPfail)(补充:其实 def/let/just 的问题只是我个人对解析组合设计的不好而已,这是在讲以后框架上可能的一个改进)。并且将会可能支持一些解析器组合时的优化/动态分支重排序/字符串扫描 SIMD 优化什么的
#blog #recomended https://coolshell.cn/articles/2424.html #statement #OOP
3)Most comments in code are in fact a pernicious form of code duplication.这个观点我非常支持。测试和文档应该描述侧面,不是你的实现本身。写过于重复的代码是愚蠢的,以用户的视角思考问题。
注释应该是注释Why,而不是How和What,参看《惹恼程序员的十件事》,代码告诉你How,而注释应该告诉你Why。但大多数的程序并不知道什么是好的注释,那些注释其实和code是重复的,毫无意义。
6)”Googling it” is okay!这个绝对赞 #statement 我很讨厌(永远只会)有问题就找 Google,抄代码的程序员,这样的程序员没有进步的空间,也说明他们不热爱自己的事业
Google只会给你知识,并不会教给你技能。那里只有“鱼”,没有“渔”,过度的使用Google,只会让你越来越离不开他,你越来越去要去立马告诉你答案,而你越来越不会自己去思考,自己去探索,去专研。如果KFC快餐是垃圾食品对我们的身体没有好处,那么使用Google也一种快餐文化对我们的智力发展大大的没有好处。
7)If you only know one language, no matter how well you know it, you’re not a great programmer.看本频道的人应该已经在自己设计实现编程语言、DSL 了(跑
如果你只懂一种语言,准确的说,如果你只懂一类语类,如:Java和C#,PHP和Perl,那么,你将会被局限起来,只有了解了各种各样的语言,了解了不同语言的不同方法 ,你才会有比较,只有了比较,你才会明白各种语言的长处和短处,才会让你有更为成熟的观点,而且不整天和别的程序在网上斗嘴争论是Windows好还是Unix好,是C好还是C++好,有这点工夫能干好多事了。世界因为不同而精彩,只知道事物的一面是有害的。
9)Design patterns are hurting good design more than they’re helping it.我们应该解决问题,不是崇拜刻板的方法论。最好的是最合适的,不是平均评价最高的
很多程序员把设计模式奉为天神,他们过度的追求设计模式以至都都忘了需求是什么,结果整个系统设计被设计模式搞得乱七八糟,我们叫这种编程为“设计模式驱动编程”,正如第一点所说,如果你不懂得用自己的大脑思考的话,知其然,不知所以然的话,那么你不但得不到其好处,反而受其所累。
酷 壳 - CoolShell
十条不错的编程观点 | 酷 壳 - CoolShell
https://coolshell.cn/articles/3745.html #Haha #dev 笑死我了
https://coolshell.cn/articles/3778.html
https://coolshell.cn/articles/3778.html
水管工2:我去拿个扳手。
事主:好!终于!等等,你就拿来一个扳手?可是你们有三个人哦。
水管工1:不这样的,先生!我还是在这里做个初始的Presentation,我一会就走了。但是,我还是会对项目的进度非常感兴趣的。我会打电话过来参加明天的 stand-up meeting。
水管工2 :另外,和你阐清一下,我们两个留下来的会分享同一把扳手,因为我们是结对水管工…… 水管工3:……你能看到这会更有生产率,我们轮流使用这把扳手。并能保证很高的质量以及持续的工作激情!
事主:我没搞懂——你们以前应该就干过这个事了吗,不是吗?500美金的出场费还不能让你们有工作激情?
水管工1:你得想得长远一些,先生。你看,我们可以一起来经历整个过程。这是多么令人兴奋的事!我对此超级兴奋!酷 壳 - CoolShell
再谈敏捷和ThoughtWorks中国咨询师 | 酷 壳 - CoolShell
/tmp/duangsuse.sock
#music 刚才在网易云听了《黑子的篮球》 OP1 ED1 完整版 啊!!!!!! 然后莫名其妙自闭了,虽然不看不科学的内容 也是 啊!!!!!! 而且科学的 #acg 《排球少年》 也是 啊!!!!!! 对我,就很不科学 说实在话一方面我操作 PC 也不是特别快,而且久坐伤身.... 我得考虑一下 可能以后不会经常学 CS 了 吧。 原因 一年.... 高考 不过还得完成最后一个.... 不知道会不会是今年 最后一个算是那种的呢? 拖延了好久.... 会被人唾弃的吧
很抱歉 看起来今天是没有办法了呢...
我有 1. 做事情耐心不够持久 2. 做完一件事情就很松懈 的坏毛病... 不过也是蛮需要体谅的,毕竟我做任何事情都很难考虑到身体
这个暑假,我在写 Promise-Java 的时候,(对 Break timer 的容忍)已经是极限了
实际上之后我根本从没有在安排下休息一分钟,像这样子一直工作 是不行了呢... (另一方面,其实我打字的速度也没有得到太多的提升,反而不如从前了呢...)
不过总之,我还是勉强对这个暑假 满意
虽然咸鱼过太多,但多少有点进步。不用再说希望,就是继续带着不确定生活下去吧~
所以请允许我咸鱼一下.... 只做最后一点必须的处理 就发上分享 😊
之后我会做一些低负荷的事情,总是挑战那些不容易想到的事情 太耗费精力了
很抱歉 人的精力是有限度的呢.... 以后尽可能学会控制自己的傻劲,必须和策略以及判断推测 结合起来吧
我有 1. 做事情耐心不够持久 2. 做完一件事情就很松懈 的坏毛病... 不过也是蛮需要体谅的,毕竟我做任何事情都很难考虑到身体
这个暑假,我在写 Promise-Java 的时候,(对 Break timer 的容忍)已经是极限了
实际上之后我根本从没有在安排下休息一分钟,像这样子一直工作 是不行了呢... (另一方面,其实我打字的速度也没有得到太多的提升,反而不如从前了呢...)
不过总之,我还是勉强对这个暑假 满意
虽然咸鱼过太多,但多少有点进步。不用再说希望,就是继续带着不确定生活下去吧~
所以请允许我咸鱼一下.... 只做最后一点必须的处理 就发上分享 😊
之后我会做一些低负荷的事情,总是挑战那些不容易想到的事情 太耗费精力了
很抱歉 人的精力是有限度的呢.... 以后尽可能学会控制自己的傻劲,必须和策略以及判断推测 结合起来吧