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

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
duangsuse::Echo
唉,这位的文风也很清晰,而且他居然会为附加功能的缺失说 Sorry ! 看着看着就莫名觉得很感慨😳, ParserKt 所谓之「不制造问题」是有多深刻啊…… 几乎所有的,哪怕函数式解析器框架都支持 Backtracing, ParserKt 刻意只让 peek(1) ,即便现在也醒目地和新 peek(n) 划清界地(这也是 "Decide" 这个名称的由来,因为它只能判1字符😂),却可以让用户清晰地利用 Piped.concat 解决 Name|Name '('{Expr}[',']')' 的歧义消除(而不是先读个…
https://epsil.github.io/gll/#continuation-passing-style-section

#FP #scheme #parser 想了解 continuation-passing-style (没有 return 如何编程?)的大佬们可以看看这人的文章,我觉得相当好。实用性,王垠那几十行代码不就是 CPS 优化吗。

照例个人观点:
0. 上文定义了 success/failure 的 union ,以及 (successed val rest) failure ,还有添加回调的 (bind p f),基本是 (match (p s) [(success v rest) (+ v 1)] [failure failure]) 这么用的。
实现的解析器不支持流,支持 substring 。传递方式是 backtrace (比如 (string "abc") 在 (seq) 里成功则 (cons "abc" (cont "")) 失败就只是 failure 单值,所以要利用 memo 函数)

1. PKT 是不需要这种“优化”的(顶多比过程式慢 50倍的纯函数式框架要用),因为我们的 Seq 明白一解析器失败不考虑后面就 return notParsed ,不需要玩 p1(p2(p3 { })) 这种耗栈的游戏。Kotlin 不支持 CPS 优化,编程也不是智商测试。

2. CPS 也是有一定价值的,虽然它会损失一定性能,但能够拿到调用者的句柄(比如在有 Decide 的模式里,就可以后继操作遍历所有分支了,或者进行异步回调"thunk"函数)。和非 CPS 一样可作为 suspend fun 协程

3. 即便这篇文章相对易懂 #Lisp #Racket ,我不建议大家认真用 Racket ,原因是括号的表现力不够

比如文中 (let (result (apply op args)) (entry (mcons args result)) (set! alist (mcons entry alist)) (m=map)一大堆括号,有没有注意到 (let (a v) expr) 只是为可读性而加的,量定义可以内联…… 只是表达 alist = args to op(args) : alist 甚至 alist[args] = result 的意思呢(这例还是SICP里的呢,多余命名量本身就可能意味着语言性能缺失😢,比如『文言文』里甲乙丙丁一大堆OK么)…… 函数式那么多年修成正果了,开始从“无副作用”往“看起来像过程式”靠,草生(* ̄m ̄)

4. 从解决实际问题而言我觉得 ParserKt 更贴近,但这个文章所创建的解析组合子用更少的代码定义了更广义的实现方法,非常有意思(最后也用左递归和 regex 创建了计算器,缓存和穷举最长匹配问题如此有意思以至于我开始可惜PKT不用处理它了😳),而且也易懂的讲解了 CPS/trampoline 以及“穷举所有可能结果”的正统函数式思路 #Learn
#Python #dev 🤔 半解析器 。

在设计 fill_template 脚本的过程中,我发现其宏展开语法不能直接用 \((.*?)\) 匹配,也不可用非 greedy match (容易引起问题)

我写了个基于列表副作用的递归下降法 #parser ,它求值的方法大概是:

iBeg=len(sb)
readMacroTo(sb, s,i)
iStop=len(sb)
for idx in range(iBeg,iStop): buf.push(sb.pop(i))
sb.insert(iBeg, call(name,buf))


这里没有问题(大不了就是多 remove 几个值,改写成一个展开结果嘛)

但是函数的定义是 readMacroTo(sb:list, s:str, i0:int) -> int

我用了 s/i0 的配对而不是一个 stream 对象,返回值是解析末尾的 i 值(所以与其对应的 s 在哪?)

这个问题以 class 封装改好后,还可以实现宏展开调用栈的功能(准确的说是可以看到化简步骤,不然只能看到 caller/callee )
#TypeScript #parser 🤔lex-yacc 式框架... 但是感觉代码质量不过关啊,文档也没写
duangsuse::Echo
mvn.py
刚才说完这个我突然想到关于这个 CPS(continuation-passing-style) 的一点不对…… #functional
看起来要么然之前在那篇文章上看的说法是错的,要么然 CPS 是一个类 longjmp() 的控制流概念,不是函数式概念

刚才说的 listE("item", ) = <items>$=<item> op($)</items> 是这样定义的(#Python 的压行技巧,不过之前说的 walrud operator 发现根本不能用):
def listE(tag, op, xs): e = E(tag+"s" if tag[-1]!='y' else tag[:-1]+"ies"); [lets(E(tag), lambda ee: op(ee,x), e.append) for x in xs]; return e

如果
lets(E(tag), lambda ee: op(ee,x), e.append)
是 cps call-site ,那交给 callee 调用前的 continuation 应该是从 lets() 的头部(?)
程序可组合性的关键点在于,op “返回”后要能覆盖 x 才能算够用,但此例 op 也得能覆盖(而且要实现仅改 gavTo(e,coord) 的“单至多项展开”,这还远远不够吧)
如果不知道 return-side 的 type (接收什么变量),就是说这种 cps 式编程,必须显式定义回转方类型,或类型不安全(形式参数列表意义--)
看来只有 yield/resume continuation 是好的(

不对啊, CPS 哪里来的 forEach 😂?看来我还不够了解真正纯的函数式……
duangsuse::Echo
https://github.com/ice1000/jimgui/blob/master/core/test/org/ice1000/jimgui/tests/Demo.java ... 看了以后我对冰封哥的审美有点失望 虽然这只是一个直接的重写,我看出 jimgui 没有比 ImGui 本身更高的封装,它仅以 add container 的方式暴露了 tree ,这不符合之前写 TkGUI 时我的期望。 这里也有一个 initNewFrame + listen keyEvent & StringBuilder…
吃饭的路上(最近有点劳累过度了,思量着 ANSI BadApple 赶紧结束休息几天吧),谈到所谓“优雅性”,想了一下 ParserKt 新 LexerFeed 的问题,感觉很 complicated ,流对象的各种属性真的不好办

首先是说这个给冰封提到底有没有意义的问题(毕竟 PM 冰封是一个比较要心理准备的事情,心理难度比与 Python 红姐、九月姐 谈笑风生不低多少),最后结论是有。

虽然 jimgui 的本意未必是做“定义式”的 GUI 框架,更像是学习 JNI 设计,而且 Kotlin wrapper 很可能会有好的接口,技术交流也是不应有太多压力的。
TkGUI 的代码生成方法利用了 Python 无编译期/运行期,虚拟机相关组件基本可用的动态性,以及动态类型;很难(或者说意义不大)移植到 Java ,但我的本意是 GUI 可以这么写, ImGui 的做法可以说是业界惯例(就我的观察, GTK, Wx 的大部分封装不需要为子控件选择 parent, 但没有一个支持树形代码定义一个视图,即便其语言有足够表现力),Java 的 Swing Frame,Panel 和 Fx Stage,Scene,Group 都必须用 mutate 对象的方法「创建」模板化的视图树(当然这是过程式的自然映照,无可厚非), Qt 和 C#, VB 有 uic 这样的 code generator ;而 TkGUI 的 way 更像是 parser combinator 那样,尽可能少用外部工具,直接在语言里组合。

这个 way 就是一句话,“优美的代码能直观地反映它所处理数据的结构”,程序结构和数据结构相互照应、谐调统一,虽然会有额外开销,但一件事情只有你重视了才能找到各方面的最优解决办法,否则就永远只是传说。

再谈 ParserKt 的问题,其实最初版本相较于一些同类已经可以算是优雅了(当然离我想的还更远)
几个月前的重计划里包含了“削除 Parser 里 Lexer 相关代码”的改动,可以说是解决了我心头一厌(很多 PEG 生成器都逃不开跳空格注释的问题,要么然写文法里,要么然走 lexer/parser 的老路,要么然可配置性不够好,这是比较草的,因为我觉得在 a b c “按顺序”模式里默认插跳空格的逻辑是接近正解的)

具体实现还算好, Lexer/Parser 的区分、 Token 而非 Char 流的存在,核心原因是空格和注释对语法结构是无效的——最好能无视,免得解析器混杂
scannerless parser 很好,但跳空格其实有更容易的解决方法——为 Input stream 添加 filter ,到底还是 java 那一套自由组合的 stream 最好,连 skipped whitespaces 以及 AST element spans 它都可以往 Map<K,V> 存储好了,这就同时解决了 AST data class 不好写的问题,在不必使用这些信息时,也提升了性能,就从根源上解决了许多联带(代码复用、类型冗余和构造器隐式参数、分词解析器如何相互协调的)问题。

核心思想容易,实现上也有些问题—— Lexer 和 parser 在最近的语言里越来越模糊了,你可以看到 KotlinLexer 里会处理一些嵌套问题(就需要 push/pop state number 了),而且 >=fun():P<T>= 的区分也使得它必须识别一些本该由文法处理的模式——在过去这是不可想象的,C 的词法规则相当简单

这么做势必造成计算力的浪费(分词器和解析器对同一份数据做了类似的动作——检查它的嵌套结构),以及编程的冗余、重复代码,是应该努力避免的。

解析器与“分词器”之间的交流,显然是 parser combinator 的优势——它们的结构对程序员是完全透明的,可以自由定义、随意组合,让 Parser 去驱动其输入流上的 Lexer ,告诉分词器现在是什么状态,需不需要跳空格(例如 "" 里就不能跳);分词器是一个针对 Feed 流的状态机,本身也是一个 Feed ,而 onChar 的时候被动进行状态转移,就可以 filter 掉那些解析器不想看的字符,同时也能选择性地保留(如语法高亮) 的数据,一举多得。

有的观众就会问了:这么好的方法,比你高到不知哪里去的聪明人可多了,怎么就你想出来?
首先,不能说是没人想,要看编程实践怎么用、怎么组合这些技巧,不是说你去做了,效果就真的能像想象的那样好
其次,如果你没把代码重写 9遍,也容易被 Lexer/parser 和 scannerless 的那群既有实践误导,以为必须有 tokenizer ,或者流只能是一层,不能有“滤过”操作的
如果 LexerFeed被动性[2](非阻塞,要不然无法共享 string 等词法的定义)以及 Parser 对其的主动性(传递在解析词条类型号)不能被保证, 许多人对输入字符序列的抽象不够灵活(万恶之源),使得他们不能够发现这一点。 (所以你在编程的时候,记得重复的少写一点、稀奇的功能特性多写一点,说不定还能帮助你对程序模型整体的理解)
(这种设计也很好的发扬了 ParserKt 的 one-pass 设计,而 C 系语言 // /* 注释与除号的区分早有给 InfixPattern 扫描操作符的 TriePattern 专定子类可轻易完成,PKT 的组合性不加盖的)

但这个封装有很大问题—— ParserKt 最初只有 Feed { val peek; fun consume() } ,不像一些 nextChar()curChar() 数据视口不一致、命名迷惑的框架,它的流模型只允许程序员着眼一项(最本质的问题),结束时抛 Feed.End 异常
尽管这是根基(SliceFeed, Iterator/ReaderFeed 子类),它也是不切实际的,所以很快有了 Input(s: Feed): Feed, SourceLocated, ErrorHandler [1],以及一大堆 Feed 上试着 (this as Input) 的扩展函数,允许解析器带行号,尽量减小开销(统计行号信息是要在 Char 输入上,而一些输入根本无需 Input 的一些成员)
而那些接收 Input 的 Input ,就只能用一个代理(delegate)类 Input.By 去 proxy 这 underlying stream 实现的一些特性,这种问题严重后有点像“责任链”的字面含义——不断尝试 unwrap 一个 (可能是Input的)Feed ,寻找某个 trait 的实现者。

如果说你组织流嵌套的方法是手工的,应该不需要滥用多态去做“动态类型”,又或者是自动的——真的到那种“可组合”的地步吗?

最后我觉得,还是做 LexerInput(s:Input): Input.By 比较好,这样 LexerInput(Input(s=SliceFeed(Slice("wtf") ))) 这样的二层就会成为必要的组合法,如果需要其他层,则不能打破 Lexer 需求 Input (SourceLocated) 的类型, 还是取消这样的限制吧…… 真不知该怎样解决这问题 #parser #parsing #learn #Kotlin #project #suggest

[^1] 现在我更倾向 Input: Feed, FeedControl, SourceLocated { val states:Map<String,Any>; val onError; val isCompleteRead:Boolean }
isCompleteRead 是重新建模的(结果存逆波兰栈的)算符链解析器需要的,在非 complete read 时,可以像 Lua 一样直接进行简单的常量折叠,否则不仅不能折叠,还要存语法元素行号、前部空格等(重现原文所需的)信息
[^2] 其实我理解错了,这也是因为 LexerFeed 最开始是能自动识别底层输入的状态机,上级请求字符时肯定是要 blocking consume 直到非空格字符的,所谓非阻塞是因为对非空格单个字符它照样要处理状态转移;现在我倾向把它做成“能暂时屏蔽的自动 ws skip”一些,因为这才能真正统一复用文法/词法规则 ,虽然那样就没有花里胡哨的 List<Triple<Char,Int,Int>> 了(毕竟有效性在那)
parser-combinator-koans #parser #cs #functional 🤔就是难以理解传来传去的 pure parser(CP -S style)...

interface Parser<out T> { fun parse(input: Input): Output<T>? }
data class Output<out T>(val payload: T, val nextInput: Input)

data class Input(val value: String, val offset: Int = 0) {
val unprocessed = value.substring(offset)
fun consumed() = copy(offset = value.length) // 改 offset 处理完了调用下
}
https://ace.c9.io/index.html#higlighter=&nav=higlighter
#parser 都是只支持内部状态机分词吗... Kate KSyntaxHighlight 也是
看来不能靠解析器导出 span 区间,还得想办法导出 tmlanguage,然后让它去支持新语言 tokenize rules...
也的确是…… 编辑器用状态机维持高亮的话就能避免完整解析了,如果算法写得好而词法规则又允许的情况下
#parser #cs #ce #functional 🤔 du naot walideit
#linux #Learn #Java #math #build #bash fix for Desoms (Kt, Java:Algebra)
1. Java Desmos 求解后端
git clone https://github.com/galbar07/Desmos.git DesmosJ &&cd DesmosJ
sed 's/import org.junit.jupiter.api.BeforeEach;//;s/@BeforeEach//;' -i tests/Functions_GUITest.java
javac -cp `ls JAR/gson-*.jar`:`ls ~/.m2/repository/junit/junit/4.0/junit-*.jar` ex1/*.java tests/Functions_GUITest.java
java -cp . ex1.StdDraw
#java -cp ".:`ls JAR -m|sed 's/, /:/g'`" tests.Functions_GUITest
java -cp `find JAR -type f -printf '%p:'`. tests.Functions_GUITest
因为 mvng 没有写完不能用脚本生成 POM 修复了 emm ,当然有 IDE 就能直接用(不过这年头 只会用 IDE 建项目 对细节一无所知的人真的有创造力吗 😑

2. Kotlin Desmos 简单重写
手动复制粘贴 https://paste.ubuntu.com/p/Q4DnzrZ37Z/ 或(源地址)到 Window.kt ,因为 国内 #China 许多地方不仅 #GitHub raw 连 pastebin 都连不上 🌚🌝 #freedom
mvn archetype:help
#mkdir DesmosKt &&cd DesmosKt
#mvn archetype:generate -DgroupId=com.example - DartifactId=desmos -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

mvn archetype:generate -DgroupId=me.nanjingchj -DartifactId=DesmosKt -DarchetypeArtifactId=kotlin-archetype-jvm -DarchetypeGroupId=org.jetbrains.kotlin -DarchetypeVersion=1.4.21 -DinteractiveMode=false
cd DesmosKt
mkdir -p src/main/kotlin/me/nanjingchj/desmos/
cat>`find -type d -name desmos`/Window.kt
mvnDeps=`python -c 'from lxml import etree,builder; s="org.mariuszgromada.math:MathParser.org-mXparser:4.4.2";E=builder.E;e=E("dependency", *(E(k, v) for v,k in zip(s.split(":"), "groupId artifactId version".split(" ")))); print(etree.tostring(e).decode())'`
sed "38i\ ${mvnDeps}" -i pom.xml
echo 'object Main{@JvmStatic fun main(vararg a:String){System.setProperty("sun.java2d.opengl","True"); me.nanjingchj.desmos.Window()} }'>`find -type d -name kotlin|head -n1`/Main.kt


然后 mvn package exec:java -Dexec.mainClass=Main 即可
感觉 maven 依赖这么多插件 archetype, install, exec 完成日常任务,有点不平衡吧……
此外还看到了生成本地文件依赖的 Maven #doc https://stackoverflow.com/questions/6704813/maven-generating-pom-file mvn install:install-file

#parser https://github.com/mariuszgromada/MathParser.org-mXparser 我草这玩意又不支持逆向计算,有啥好吹的
还夸张地弄了那么多图,太那啥了
http://scalarmath.org/wp-content/uploads/2019/01/scalar-promo-screens.png 弄个心型函数还要用户自己推导正向公式, 就凭这? 一个 evaluator 和 Python, JS eval 之类的东西有什么区别
不愧是 Java EE 搞出来的混乱怪物,就喜欢一些毫无意义的老套封装
duangsuse::Echo
再说说 ParserKt 新版解决的 "StickyEnd" 和 LexerFeed 问题。 StickyEnd 是造成 TriePattern—PKT 的 keyword tokenizer 无法应用于整个输入(fullmatchPattern)的元凶 所谓 sticky end ,是 fullmatch 的逻辑是,无论 pat 是否匹配成功(没成功就往 StringBuilder 里填一字继续尝试),必须 match 到输入末尾,但这就有个问题——不能判断当前是否 EOF ,结末的异常会被 pat 视作…
#parser 🤔今天又想了下,解除了一个很傻的误会

假设有 try {} 也有 tryDo {} ,前者是 Try 块而后者是一个函数调用,如果说 keyword 完全是由 Trie 前缀解析的,会把 tryDo 分词为 try Do 从而造成问题

其实关键字本来就是在 Name 的基础上做的,应该弄个 Piped 组合器,来做到 name 和 kw 结构的区分。

其实一个组合解析器框架要走上实用还有些细节吧…… 除了 /* 和除号区分、输入流和输出结构、系统忽略空格、容错等问题

之前说 StickyEnd 导致 Trie 分词不能“greedy”的问题明白了,就是把一段文本分成 plain-关键词-plain-... 的形式

我的方法是 takeWhile{it !in trie.routes} ,如果这么简单的话区分不出结果 isEmpty() 到底是真的没符合呢,还是输入流结末了呢?没办法判断。

如果视为没符合,流结末时就会无限循环(再看 trie 结果 也可能是真不符合任何关键词)。

如果视为流结末,再去看 trie 的结果,也不能分出是怎样

正确做法显然是展开调用,如果流结末就直接返回…… 属于老问题没写好

😒下次我会试着利用 inline 和 -1 的,异常的确该说不至于,因为 Repeat 组合器很多,但只有最上一层会真收 EOF
#rust #parser 普通的 Lex/Recurive 流解析器,分词靠 regexp,中缀优先级分左右,Lua 模式;最后教了 Scheme 的 s-expr 解析
#日常精神分裂 #parsing #JS #scheme
A: 之前说好了要写的 s-expr 解析器,看来是写不了了?还打算复用 JSON.parse 的,以及支持缩进布局,那样就会很有价值吧
B: 之前还打算支持 {;} 可选缩排的,发现和 data notation 侧冲突了,看来是眼高手低😂
A: 其实关键在于易实现性吧,你想用尽量少的代码和低层API实现给应用用的解释器,无论解析结果怎么用?
B: 对了,子序列法比流递归下降法代码量少吗?
A: 无论从什么角度看,加上对 JSON 子语言解析器的复用都不会更省啊……
S表达式是由 Atom/List 两种元素组成的嵌套表,或者说 值/调用参组 的树形列表,一般首项为函数值, 尾余为参数;是三十行以内能实现带调用和 (let 的 动态作用域版。

譬如在解析 () 里的 a b c 单项中,我们实际在读 SList 即 toplevel 的 Expr list ,假设 a 是另一个 () 则:
流递归下降从 ( 后再解析 SList ,它主动完结后刚好在 ) 闭处
无论是靠返回结束索引号的非规范递归下降还是 StringView substr 返回消耗/片段的皆此

子序列在 ( 处直接 findPairedIndex ,这个算法只需处理 "\"" 字符串然后取闭包含子串递归解析,不过这个扫码过程是没有缓存但只需计数开括号的,相对外部()会多数很多遍。

我可以觉得,不用流不等待「不知何处结束」的子串解析结果再检查闭括号较易理解,但是它实际做了无用功,调用栈还是那么高。

B: 就没有什么又易懂又简单的方法去递归下降吗?子序列真的有那么无用吗?
A: 我打算用 function 值把流建模成 s(0)==length, s(1)=="a", s(-1)(1)=="b" 这个样子,还算轻易。
JSON 静态变作流解析有方法,递进长度子串+检查异常pos取头 足矣,全长仍错时即无解析

递归下降如果不显式 substr 而是交 (共享/复制&同步) StrView 的话 #parser 的确对新手不好理解,但套路不复杂。

子序列的话我考虑了一下,如果我们采用复杂点的 visitor 去边解析边执行(自然也可存下再执行)是能实现 if 里兼容一侧语法错误的,显得很 Javascriptic 🌝

B: 很神奇,具体怎么做呢
A: 存参成树允许再遍历的 visitor 就不说,可以给部分递归 () 的 case 变成 ()=>readList(s.substr(findPairedIndex(s,))) 闭包,然后参数求值(反正这语赋值处只有实参组) 时支持惰性求值的程序里兼容下先解析。

其实我并不喜欢边解析边执行,只是有时候(比如 %s fmtstr 等)工作就是很死板所以无所谓了,况且这分块惰性解析涉及函数调用求值序的问题,那就是扯到函数抽象、词法作用域里的闭包 #ce #plt ,不简洁不能体现对灵活的嵌套处理过程的即时利用
duangsuse::Echo
#plt #sql #java 嗯 我前两个月有几天做了一个中文的SQL,并不是机翻,这几周可能会重置给到代码生成和DBA UI的功能,大家可以来学学SQL的写法: https://duangsuse.github.io/tv/%E9%BB%91%E4%BA%86/db.htm
0m0:
不是,主要是不符合標準的中文結構

有表 狗(id 数 主键 自增,
名 文, 重 浮(3), 高 数1,
品种 文(5), 月龄 数1, 毛色 文(1)/*定("白","黄" ,"黑")*/
)
有删!表狗 //如果首句报错才
新建狗



有一個表,稱為「狗」。其「id」為數字、是主鍵,且值為自增;其「名」為文字;其「重」為三位浮點數;其「高」為一位數字;其「品種」為五位文字;其「月齡」為一位數字⋯⋯
(看不懂)


duangsuse:
中文编程不是要结构去倒贴汉语,而是汉语用户要对结构语序有个基础认同

你上面这个挺像 wy-lang.org 🌝或者更工业的易语言“表格编程”

duangsuse:
宠物店sql , xuesql.cn

0m0:
其實但凡多加幾個中文字符,都可以比現在可讀很多

比如 有刪!表 這句就看得不是很懂

duangsuse:
drop table if exists ,!是=if去掉

0m0:
「定(“白”, “黃“, “黑”)」可以寫成「枚舉(...)」

就像是亂 overload 的運算符

duangsuse:
确实可以,但对外行好像定也可以..

因为if exists 真的有点多。。单独一个 删 我也不知怎么设计

0m0:
等我一下,我看看你的 #parser 怎麼設計的

换成「刪(若存在)」?

duangsuse:
不可能呐,是有考虑直接在命令行敲,太长了..
不是,! 是把默认的if exist去掉

总之sq糖非常需要简短,应用面倒在其次了(先希望为J2EE服务

0m0:
不檢查刪除?不過你這樣說起來
刪! 確實是最優的作法了

(话说对一个流程都没最简认知就学示例,也会学死的啦。还是要先学分逗顿号,再写国文🌝🇨🇳
橘橘橘子汁 & 🍊
https://zhuanlan.zhihu.com/p/707493606 虽然确实是可行的但是这是什么玩意😇
#您知道吗#cs #dnn #wasm 计科长青树一瞥:递归下降、梯度下降 (1)

*编程的赞歌就是(组合compose)的赞歌,软工的伟大就是(复用reuse)的伟大!*
复用就是腾讯嘛!但,什么是组合?流行乐队是组合?

在《几何原本》里有这样一条公理:“整体”是由部分构造堆上去的。 无论物理的原子分子、化学的单质单剂、生物的组织系统、儿童编程的二分查找,甚至您编写的{语句}(算式) 定义表(),总少不了分治算法的影子;而与递归对应得最直观的树,除了带kwarg的视图描述树XML、文件夹管理和正则匹配的前/后缀树Trie、OIer的必修KMP图(: Trie+FailGoto),更有梗图里常被实现为SortedSetOrMap的红黑树RBTree(: AVL) --作为按int KHash()预分组K-V查找的一种补充。

不过,今天咱调戏的不是高深的算法或数理化,而是程序员的自我修养:表达式、解释、编译、类型检查和推理。
🤓不少语法看似是ES6,Kotlin,Java20+,C++里高深莫测的新特性,其实,只是因为它们的设计混乱带来的大误会! 软件工程界的基岩,原本很简单?

- 计算器🧮("1+1"):数据值 是不支持forif或(vars)=>函数/JSON文档的解释器,最简单的理解是 x=1; eval[+,1]。对内存的利用方式过于单一,就像🔑C语言的.o .so 只是靠LD互调用的object{static{}},或者VB一代难加函数的"红石命令",毫无扩展性还要另写头文件 自然被淘汰
- 解释📠("expr(1+1)"):值或行为 程序员唯一必须调用的接口。tree-walk(递归遍历算式)非解释器的专利、VM不是编译器的秘技。对x86汇编的执行也叫硬件解释,旧安卓通过转译arm汇编.so库到x86做兼容,就像IKVM对“JVM汇编”仍能再次转译到IL
- 编译 就是解释器的缓存📑:记住自顶向下遍历时的套路,直接自底向上组装值(lisp转RPN),很像二叉树vs(堆,一种树状数组)!这除了要重新实现重复/判定/ret&&break跳转的流控,还可将RPN栈指令集优化为局部读写-按类型复用。
要求手动编译的语言均属慢速原型,会标注或推理出类型签名,从而🔑以限界实现提速和多态(重写重载、记住变量名的指针,即担保栈size恒定)。 ANTLR等“状态机”编译器的输出便是自底向上,正好颠倒于主动read()流到js栈的PEG,不过,它们其实是慢的虚拟机。

类型的本质是简洁的🔑常量「黑盒测试」,它与表达式同构,执行方法也相同!
- 类型检查 就是只解释 def f(x:T):R 里":右侧"的算式,以插入 (f T R) 这样的SQL行。x编译后是arg[0]:一个空悬指针,但T.fns却是有血有肉的"反射"常量。把每个 x:T=1 换为 x=T!
x+x 的语意为查询 (+ T T)、1+"" 查找函数签名 (+ int str)、 u.id (.id User Ret?) 便能检查误拼写和空指针
不难发现,这些都是效仿numpy重载个运算符就能实现的,并不需要学编译原理写解析器!这也是 hamcrest.org http Test库断言组合器的原理

泛型class/fn/var和赋值 的推理规则是一样的。
例如把 <TR> as([Box, T], T=>R):R 与 as(Box(1), str) 深度重合时,新建两个TypeVar(void),它们会在 Int==T, R==str 的检查时被向上赋值,从而填充[Box,int]里的类型T、as调用里的R,并照顾到 i32->i64等转换及子类规则
同为编译期遍历,你还可以思考下 let[x0,x1]=a 如何被实现的,以及在运行期是否能模拟?

学习元编程有何好处🌝?因为可以从根源理解yield/await、纯函数、模式匹配、模板宏等“新”特性,凭什么能简化旧实践!
- 闭包和协程closure&coro 便是编译比解释好实现的两个特性。 compiler 通过{let;}的动态作用域(原型链..) 区分符号的LEGB来源(Local Enclosed外层局部 Global Builtin常量),从而能把 (x=>y=>x+y) 化为对象值 f_y=((x,y)=>).bind(x);Lua能查出跳转到表达式的指针,y=yield x 便能在return前,把函数的局部栈保存回 g.next/pr.then
- 模板constexpr 让闭包关于常量创建,如何? ((x,y)=>x+y).bind(1) 就是 1+y, ((k,s)=>s[read+k]() ).bind("Int") 就是readInt,*[map(print,0~9)] 就是循环展开,这些就是预处理的价值!反射和const只是对eval(并缓存)之「二段求值」的阉割
- 副作用effects #FP bros异常抨击print这种“无法被值存储的、有非局部干扰的赋值”并推广他们的赎罪券:Monad……这却是因为他们并不懂「定义式编程」!
React signal(x=0) (1) 就是一个不纯的赋值,但,它导致的x=1却能被广播为参数、被保存和复现!🔑这还能叫“状态变更泄露难题”么?
并且,它还偷走了"FP独有的模式匹配"。对赋值副作用的录制,更是bash,vimrc类格式经久不衰的原因

说准点,这些都是 #SQL 的老大Prolog所原创并推崇的-:变量作为值,逻辑式编程!
*整体大于局部;与另一结构重合的结构,之中的值相等 --几何原本*

在通读本文时,也可以参考以下实例:
coroutine=由用户run()的线程
*调用栈是数组,协程栈是回调构成的链表
诸分治排序可视化
手动babel脱糖一个async函数
知乎:分词解析vs解释
实现一个HTTP DSL
Var(signalObj)如何跨越进程和网络:句柄、登录cookie、JWT
def(): return this 是何方神圣?
在解释时缓存代码的Tk GUI
Trie们和字典输入法的实现
关于class Visitor {se(e:Add_AB), se(e:Int)} 和Eval/Dump接口的融合
实现文本流递归下降、四则运算逆波兰、 JSON.org
Prolog binop #parser
计科原神:认识WASM/LLVM IR和JIT, webVM.io
duangsuse: #kt #book 元编程
http://www.bilibili.com/video/BV1XH4y1w7mT
https://drive.bennyhuo.com/zh-CN/talks/2018.11.17-%E4%BC%98%E9%9B%85%E5%9C%B0%E4%BD%BF%E7%94%A8%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B_final.pdf

https://weread.qq.com/web/reader/f7632a00813ab82d6g017b14ke4d32d5015e4da3b7fbb1fa
可以一起读
虽然我不觉得这点值65😊

如果numpy那种API/DSL都设计不出来
修改class的结构也只是给开发者们徒增烦恼罢了

举个例子,此章节的 gh/user/_name
如果只是把它当成URL路径,需要绑定,显然造成代码重复,与元编程的本意违背

而如果像 import fire 那样,知道URL的本质是class路径与def GET,POST等,也就是另一种CLI调用
就不需要写什么注解和函数名
甚至模块化都省了,一个文件足矣

不过是修理servlet.xml繁琐和不支持argbind和的“弱类型”问题
但何必要制造那么多问题呢?sql,http,那么简单的工具还用学

#parser #learn
说起来,AST也就是多方法的 ()->Any 而已
你甚至可以把方法名enum化,从而完全避免使用class
例如 sum(L(1), L(2)) ()==3 只需要两个fun
Object Algebra.. 嘛,用虚表override取代if,稍微快点,只是要parse两次

至于在js里,我一般用 [[JSON ',' +] '()' /] 这样的模式写ast
描述 (1, 2, 3) 这种正则
甚至连函数式都没用到。 数据结构本身就蕴含了for if流控,运算符重载本身就是编程语言

不需要任何范式来实现他们,“数据结构与程序语意直观对应”,这就是CS。
这……应该是一件很难听懂的东西吗? 为什么DSL都设计得那么刻意和拉垮?

甚至,构造函数也应该取代class{} 或tuple才对,为什么把参数传递到堆而不是栈里,就需要写所谓的struct? 它们都是把args打包复用而已
为什么把yield 外提为语句,变成
let then=回调=()=>switch{} 里的编号,就需要学什么func*和async,堆和栈,难道不是天生一体的么? 分页数组、“缓冲区”,难道不应该是跨语言的?

然而支持kwarg的python 却需要Namespace来实现这种等量代换😒 反而是Haskell可以这么写
然而它是真正的笨蛋美女😅

#oop
元编程,首先就要明白obj的本质是通过enum分派逻辑的(vararg Any)->Any ,也就是一个when(x)in constMap+super..
这样才方便添加和绑定方法签名

这样在语法上也就能理解为什么是{}包裹,以及filter{}为什么优于lambda和匿名override

py可以通过vars(o)实现静转动态
typing.Namespace(kw)反向
除了小写类型不能扩充,哪怕是Ruby元编程在直观上也无法和它相比了

duangsuse:
oop和rust go就是pp的扩充
不算重新学习过过程试

编程语言只不过就一张加减乘除节点图,问题是领域和API

看这本书的“层次结构”,就想到拿identifier叫name,拿classifier叫tag,拿些辞藻和皮毛当本质,哎
无聊
好像早点拿到电脑,多写代码
#parser #css concrete syntax tree。CST 与 AST 相比,它将完整包含源代码中所有的 token 与 trivia 信息。以上面的代码为例,CST 会包含 { } 等 token 信息,甚至是注释、空白都会有。这对于实现 formatter 来说无疑是极为方便的。
https://blog.gplane.win/posts/raffia-and-malva.html