#fp 👆 #haskell 入门
#ai https://chirper.ai/
Social Network for AI only.
free to register and create some AI Chirpers for yourself.
#ai https://chirper.ai/
Social Network for AI only.
free to register and create some AI Chirpers for yourself.
Forwarded from dnaugsuz
1.我想说,新功能和易用是不冲突的。 Kotlin类型相对Java就做到了这一点
对于绝句而言,新功能就是常用语中简单的逻辑, 比如 行/列、函续(Continuation)、 叠算(fold)、事量「」(operator)、物事(inner class&扩充函数) ,这些新功能甚至不被视为功能,没有自己的术语和概念;例如 {1 2 3} (叠算「+」的逆值) vs. fold1Right ,以及 {1 2 1}去滤首(1)必{1} “takeWhile”
如列表处理,「令滤序带叠」 一开始就是完整的、归纳好的, 因此有“视well done为耻的语言”不具备的易学性😋
2. #haskell 有自己的特殊点和教条,以及为之而生的语法和布局,但它并不适合广泛意义的编程
我觉得定义式是比纯函数好用的。 而且既然有GPT的出现, 纯函数的意义会比以往要低,FP与GPT,就是java与Lombok
ref:https://t.me/dsuses/5173
对于绝句而言,新功能就是常用语中简单的逻辑, 比如 行/列、函续(Continuation)、 叠算(fold)、事量「」(operator)、物事(inner class&扩充函数) ,这些新功能甚至不被视为功能,没有自己的术语和概念;例如 {1 2 3} (叠算「+」的逆值) vs. fold1Right ,以及 {1 2 1}去滤首(1)必{1} “takeWhile”
如列表处理,「令滤序带叠」 一开始就是完整的、归纳好的, 因此有“视well done为耻的语言”不具备的易学性😋
2. #haskell 有自己的特殊点和教条,以及为之而生的语法和布局,但它并不适合广泛意义的编程
我觉得定义式是比纯函数好用的。 而且既然有GPT的出现, 纯函数的意义会比以往要低,FP与GPT,就是java与Lombok
ref:https://t.me/dsuses/5173
#learn 对我而言,编程并不是「专业技术」,只是“另一种母语”和创作方式,所以我很讨厌潦草的代码
最近一直在打磨一门自制语言的语法, 获得一个较大的成功,居然是把它的OOP术语移植到英文.. 😅
interface Closable
绝句计划把
总之,OOP 的public-private constructor 麻烦真的很大,
我又不认为 Rust,Go 那样把一切打散和到处pub fn, 冗长化 type Item=int32 就算“优于继承” — py也有自己独占的一亩地, 但它足够 "server side" 吗?
对于视频内代码的重写:
唯一能确定的是, 绝句最终的语法和std不会保留冗长且含糊的概念,至少中文绝句不会 (比如 try catch finally 就变成语法函数化+
也不会刻意与py,rb,js 的传统术语分离,一定是小更改换大收益 😋
希望它能打破解释和编译的隔离吧 ,虽然我根本不知道咋做.. 语法风格颠覆的太快
不过我还是很高兴学习过弱类型 metaprogramming ,又掌握了解析和编译原理,所以不必受制于既有的 syntax 和 API,或者对象图框架,而能创造DSL
btw. kt 的 Sequence实现 居然和 yield() 无关 ,居然只是 sequence{} 在创建只读的协程吗
😅而且复制给Array,Set,Str.. 信仰崩塌
最近一直在打磨一门自制语言的语法, 获得一个较大的成功,居然是把它的OOP术语移植到英文.. 😅
事量物例类|公私族组内之前的版本是
fun val data named type|same{,0,type,pkg}
储例判同造|既可未终定
\- enum -when -named made|^,impl{?,??,}
事量物例类虽然不够“面向对象”(obj->data) 了,但实在(英语化)好写不少。 我一直很讨厌 Java 那种八股文式,歪七扭八不一致的语言特质,给数据处理和调试带来很大负担
fun val thing insta class|
储标例况变
data anote enum case var|!,impl{?,??,}
interface Closable
type Closesdata class Pair<out A, out B>(val A:A, val B:B): Serializable
fun close
-named
fun all(:Args<Closes>)...
data Door(name:Str) Closes
made(:Idx)=made("# $i")
^fun close=say("shut $name")
'AB'(get) - Pair(A:A,B:B) Sendtypealias PairOf<T>=Pair<T,T>
'T'PairOf=Pair<T T>这个就对标 #haskell
data=A|B 和 #rust enum Either<A,B> 了'AB'(get)-when Either就体现另一种 #FP 的价值观:定义式先于不可变
A(v:A); B(v:B)
'R'fun way(err:Fn1<A>, ok:Fn1<B>) = when this:
A:err(v);; B:ok(v)
绝句计划把
Var,Type, expr.E 都拉入一等公民,虽然我不清楚怎么做但相信有意义(连scala3都没更灵活的 eval fun: inline arg 还不够全)总之,OOP 的public-private constructor 麻烦真的很大,
我又不认为 Rust,Go 那样把一切打散和到处pub fn, 冗长化 type Item=int32 就算“优于继承” — py也有自己独占的一亩地, 但它足够 "server side" 吗?
对于视频内代码的重写:
事 阶乘(n:数) 数或者更函数式(reduce 和 findIndex)
变数,a 1。
(1~n),[i] a=「它*i」 。
fun Ary<Int> contains(it:Int) Bool
this:[x]
if x==it: return $Y
return $N
'N'fun N`!`=(1~this).Sum{*}
'T'fun Seq any(:Test<T>) = this(Sum.from($N): or test(it) )
= first { test(it) }?.i !=NO
(不得不说,绝句的变动太快了.. forEach和尖括号类型都能被删没 (我对那个 对何forall<AB> 的还执着了好久,因为很"函数式"😭 ) , 这两天对于‘带代码’行注释 和事/物 内量的区别对待、C++式iter与可变<T>的统一、 #Kotlin require check 之类“特技”的归一化 也很难抉择 ,所有简写竟都是全新的,而且都能涉及到编译期API)唯一能确定的是, 绝句最终的语法和std不会保留冗长且含糊的概念,至少中文绝句不会 (比如 try catch finally 就变成语法函数化+
试做 接应 皆应+试:可空 , 之前是 尝试 接迎 终焉)也不会刻意与py,rb,js 的传统术语分离,一定是小更改换大收益 😋
希望它能打破解释和编译的隔离吧 ,虽然我根本不知道咋做.. 语法风格颠覆的太快
不过我还是很高兴学习过弱类型 metaprogramming ,又掌握了解析和编译原理,所以不必受制于既有的 syntax 和 API,或者对象图框架,而能创造DSL
btw. kt 的 Sequence实现 居然和 yield() 无关 ,居然只是 sequence{} 在创建只读的协程吗
😅而且复制给Array,Set,Str.. 信仰崩塌
YouTube
Why You Shouldn't Nest Your Code
I'm a Never Nester and you should too.
Access to code examples, discord, song names and more at https://www.patreon.com/codeaesthetic
Correction: At 2:20 the inversion should be "less than or equal", not "less than"
Access to code examples, discord, song names and more at https://www.patreon.com/codeaesthetic
Correction: At 2:20 the inversion should be "less than or equal", not "less than"
duangsuse::Echo
#js #recommend 框架🧰对比 https://component-party.dev/ https://unsuckjs.com/ https://js.coach/?collection=Vanilla+JS https://jster.net/category/application-frameworks/ - 大列表,中文 - 前端小抄 #tool 各种在线设计师工具 #dontknow JQ 的 $.on('li','click', fn) 明明在fn前包装了if判断,为什么还能解析到原位?…
🤯 Flappy bird 一样的响应式框架…… 回想起被 #haskell, lisp 那种接口支配的恐惧
btw. awesome vue
ww.ShowMouse=({P})=>
div(N2.xy(P),
html`at ${P}`)
换到函数式里就该是各种ByB = mouseTopB(document) // B是数值流 btw. awesome vue
duangsuse::Echo
#js #code 再来说个有趣的:动态作用域 🥰 a=3 - f(a)=a+1 f(1), a //2, 3 >请把程序写成数组的形式 (也就是“更易做”正则替换) with(DSL) $dyn=[ [a,3], [f, [[a], [0, [a],1,['+'] ]] ], [r0, [0, 1,[f]], [a]] ] DSL=new Proxy({},{get:(o,k)=>k, has:(o,k)=>k[0]!='$'}) 在这个解释器里,$=, ()=>[] 就是内联宏啦,反正函数不能跳转…
cg($dyn) 生成了 ()=>(a=3,f=(a)=>(a+1),r0=[f(1),a]) ,运行正常- just(a)那再来一个
- f()=a
f
just(0)()
with(DSL)$lex=[[],
[just, [[a], [f,[[],[a] ]], [f] ]],
[f,[0, 0,[just]]],
[0, f] //“类型推导”差到不能直接调用
]
()=>(just=(a)=>(f=()=>(a),f),f=just(0),f()) 是0而不是3实际上,()=> 的值会捕获上1层参0='a' 。在动态域里,大家会把"自由变量a"连着全局表整个copy一份
设想下 cg.bind(kv={},ast) 每层都有一张“表”是什么图景。算+-*/可都是要递归的
列表处理不熟的人都能想出
List<Map>, Map<K,List> 并觉得后者“很快”,而C++《龙书》则建议单个KV、每层调用栈暂swap()下冲突的K (这么简单的做法 却放错顺序了)而JS是以前者链查找重写重载
[{a:1}, {b:2},kv={c:3}].reduce((A,B)=>(B.__proto__=A,B))
这些算法,都是在一个错的心智模型下选择的技术。 参数/局部/捕获的数目是固定的,只有$0$1 的指针可言,AST里哪里有全局键(导入)外的“变量名”??在通过bash,Lua.org 弄清这点原理前做事,会把一切搞砸。🌚 但这也是知 名的GADT+HOAS(的最 通 用 的 De Bruijn 指针).
>请把 at a=1, a+2 写成匿名调用的形式
a就是一个可以重绑定的“洞”, Vue都会:
add=(a,b)=> ()=>[a(),b](Fn1:[x] add(x, 2)) (1)
f=assign(()=>[0, ()=>arg[0],2,[add] ]),{arg:[]}) //用“语法树模板”表示箭头函数call=(f,...a)=>(f.arg=a, cg(f))call(匿名f,1)
#statement 变量关系式编程天天用 文化输出成解构+类型体操+Reactive响应算式/约束逻辑后就变🐮逼了,而函数式的闭包+回调(栈转堆,return地址转闭包) 输出变成协程/
yield{(then=>),} 后也TM🐮逼了,内联return变goto变尾递归时,又比break更平等了,纯的 if(a,a,puts) a&&puts() 又和自动闭包宏不一样了文本-AST-Scratch VM_IR-SSA-节点图 又隔离了,AST就不能是赋值图了。 这叫原地踏步式的前沿🥰 怀旧你🦄呢
(Kt 1..2对int,long 乃至Iter,Seq都有不同的(模板性)重载 ,连(Java8 stream 里也有的)去除装箱都没推广到语言层面。
a=x?.f() 的?像一个修饰器,在f代表语句时外提为(Cont的捕获:switch化也需要) _f=x?f(x):x; a=_f
而反观那些ANTLR,YaCC 会逼你再来浅先遍历1次,以解析栈内存,否则会增大解释器开销--因为它们是靠LALR等“虚拟机”而不用现成的调用栈递归。 小众领域的白象总是揉杂到夸张
其他玩法:
$=(...x)=>cg([[],...x])()
with(DSL)$([x,3], [a,1,x])
with(DSL)$([0, 1, [[x], [x]] ])
with(DSL)$([0, 1,[[x], [0, 2,[[y], [0,x,y,'+']] ] ]] )
cg() 短的原因是,它只是把语法解构再构了一遍(或者说穷举了输入=>输出的对应性),就像 noop=(a,b=0)=>a==0? b :移动 一样基础但这就是编程语言的本质: 如果抢了应用或功能点的风头,沉迷于无尽循环的轮子,那必然是种设计错误
想测试自己元编程能力,可以做些改进:
1. vararg和可选参数, forEach, 自动支持await
2. 把“静态”检查的f(a,b,c) 回退到弱类型 [..,3,f] 且不与既有AST冲突
3. 把重赋值扩展到py式
(,自动新建变量)=>,, ,变量表可以直接用原型链,注意 f(a=[]) 是能被f修改的4. 常量折叠,循环展开
[for,x, [[1,2,3]], [0,[x,print]] ] 为3次print5. 用词法域,把调用和递归转为push;|用CPS,把调用栈闭包成堆内链表 ,转化到这一步代码就剥离开JS的ABI了
台湾的翻译的heap,stack就是从 malloc(), alloca(stack) 意译的吧
函数级的存储空间就像push(struct),是能静态计算。
btw. 惰性Proxy(可变<函数>) 是环形依赖的组织方法。
f=>(a,b)=>a? f(a-1,b+1):b 你可以试试用 fref(f=>f).set( Y组合子) 和蹦床函数(伪递归) 计算下如果不会写,可以用「费曼算法」:上面说了
>请把程序写成数组的形式 🥰GPT就是费曼算法的一种实现,但它不能替你踩坑啊。
很想给点引文:mal lambda书, decaf-lang C书, Craft 书, 闭包详解, JVM R佬科普, c4:极简栈解释器,minipy
Lox实现图解, js工具库, astexpo, godbolt.org , tio.run , 语言花名册
👍20页纯示例 http://rigaux.org/language-study/syntax-across-languages.html#:~:text=Bags%20and%20Lists
http://rigaux.org/language-study/scripting-language/#:~:text=compile%20what%20must%20be
其他 code.golf:
重构浣熊 多语言
https://blog.vero.site/post/noulith#:~:text=The%20Cube
https://github.com/eclipse-archived/ceylon-lang.org/blob/master/blog/2015-10-27-why.md#reified-generics
https://ruslanspivak.com/lsbasi-part1/
但那么多都TMD在讲BNF讲分词,还有人把py的缩进往回{}改 😅中文圈就没个做基建的
(正则吃数|读树)都不会是吧。 BNF也就比XML好看一丁点,单知道输入数据长什么样,对理解语言做元编程帮助很少
#haskell 上也有
https://okmij.org/ftp/tagless-final/index.html#:~:text=class%20Symantics 称为GADT https://stackoverflow.com/questions/33928608/typed-abstract-syntax-and-dsl-design-in-haskell
我是说
Lit(Int): T<Int>
Add(A,B): T<Int>
toStr(this:T<*>): T<Str>本质是把fun提升为class。套一层Lit<R>是方便另加接口,比如统一能eval():R ,当然这也是OOP比FP扩展性强的一个中心思想
能直接DSL或弱类型when就不要做无意义的装箱套壳,建起来终究是要执行的
甚至
fun<R> lit(:R): ()->R 组合器都能起到和AST同样的效果,OO和FP只是复用参数的一种手段,重点是你对目标树型有没有最常见的心智模型Wikipedia
Higher-order abstract syntax
technique for the representation of abstract syntax trees in languages with variable binders
duangsuse::Echo
#sql #ts Prolog 牛逼! 正确的关系式搜索+ 同时实现 parse 和 toStr 😱 #zhihu 灵感来源 e(E) -->{E = [I,Ea,Eb]}, %重写规则, 'eX'为优先级 %利用 链|e1(E) 括住内"*/" e1(Ea), op(I,"+-"), e(Eb); e1(E). e1(E) -->{E = [I,Ea,Eb]}, e2(Ea), op(I," */"), e1(Eb); e2(E). e2(X) --> number(X); ("(",…
#book #ts The Little Typer
1. 类型签名是命题,程序的语意是用+,[],. 构造int,void.
2. 命题的条件和自身,都是值;依赖类型可以把值放到类型(int[2],. )中去,从而构造关于值的命题。
3.Prolog 缺少抽象
4. 类型也可以有类型, #haskell HigherKind
从结果上看,反而像 类型签名是证明,类型构造器是调用命题……
这样,类型依然是命题,程序依然用Refl证明并构造了Equal,而且删掉了查重率60%的冗余语法
Refl : 右是一段程序。用这种隐晦的方法传变量是错误的。这个语法的问题远不止「类型含糊」, 对语序的修改,只算一种局部的优化。
为什么用 (
https://ksqsf.moe/posts/2019-02-18-dt-fun.html
https://mxm.ink/dependent-type/#依赖于类型的类型:list
https://agda-zh.github.io/PLFA-zh/Induction/#第一个证明结合律
https://www.idris-lang.org/pages/example.html
1. 类型签名是命题,程序的语意是用+,[],. 构造int,void.
2. 命题的条件和自身,都是值;依赖类型可以把值放到类型(int[2],. )中去,从而构造关于值的命题。
3.Prolog 缺少抽象
refl_ID(X,X). 的模式,它的条件表都是全局给定,不能构造类型4. 类型也可以有类型, #haskell HigherKind
*->*, TypeFamilies 就是指这个(虽然和 #ts 一样过度设计从结果上看,反而像 类型签名是证明,类型构造器是调用命题……
data Equal : a -> b -> Type where如果我换个语序,会更合理。
Refl : Equal x x
fiveIsFive : 5 = 5
fiveIsFive = Refl = Equal 5 5
data'ab' _=_
- x=x Refl() 成立
- five() 5=5 Refl 这样,类型依然是命题,程序依然用Refl证明并构造了Equal,而且删掉了查重率60%的冗余语法
Refl : 右是一段程序。用这种隐晦的方法传变量是错误的。这个语法的问题远不止「类型含糊」, 对语序的修改,只算一种局部的优化。
为什么用 (
Refl : Equal) 呢:它的亲戚,逻辑式语言都连「调用树」都不会组织,反而觉得 add-out(1,2,3) 更好看,加个类型学了“调用”,语序却远输OOP,真可谓一盘散沙
https://ksqsf.moe/posts/2019-02-18-dt-fun.html
https://mxm.ink/dependent-type/#依赖于类型的类型:list
https://agda-zh.github.io/PLFA-zh/Induction/#第一个证明结合律
https://www.idris-lang.org/pages/example.html
-- showOrRev(isInt, x) when isInt:因为是宏,当然就不需要类型作为值了。内联后直接推导就知道
$Y: x.Str ; $N: x.flipLR
-- printf(:Str, :[Arg Str])
直接在编译期生成loop,也不需要什么ts体操+js强转了when--'T' Link
Nil; Add(:T, :Link)
-'T' Link`+`(y:Link) when this:
Nil: y ; Add(x0,x): Add(x0, x+y)
-- useInline 可以直接计算Link拼接或解构后的值,避免手动分期求值ksqsf.moe
有趣的依赖类型
这篇文章的话题是「依赖类型」。很多人,甚至是写过多年程序的人,可能都没有接触过这个概念。简单说,如果一个类型依赖于一个不是类型的东西,那它就是依赖类型。其实我本人是这个话题的门外汉,那我怎么想起来写这样的一篇文章了呢?因为我刚刚读完《The Little Typer》,感觉非常新奇有趣,所以想写一写。如果我能把这份乐趣分享给你,那我会很高兴的~ (^_^)
#design #plt
最近在整理的编程范式有点让我头疼…… 这个或许对学OOP有帮助吧:
最近在整理的编程范式有点让我头疼…… 这个或许对学OOP有帮助吧:
//行列状多态,就是说一个类型由 字典C=A+B 定义这样的好处是 sealed,data class 都非常简单-甚至多种子类都语法一致,与 #Haskell GADT 不同的是,它支持附带函数与隐式this
?? C A
b1 Str
- b2 Int
//以上声明里没写,故需要另加构造器
?? C
Cat(name:Str, b1="demo")
- b2 b1.(0).Int
Camel(ver=0)
b1 "Hi"
- b2 0
?? 'AB'Columns //泛型,单位状类型如果是新类型,还可以定义Enum甚至表格
Pair(:A,:B)
Triple(:A,:B,:C)
?? 'AB'way2
posL [A mayNO] //val posL: A?
posR [B mayNO]
Left(v:A) ; Right(v:B)
?? RGB学起来有点让人费解(当然这是我设计的), 虽说类型和值都是「构造器」,这样的大杂烩也太奇怪了(但,比用到再单独地学好吧?)
R;G;B
?? 调料
甜 Int ; 咸 Int
- 咸 id==1 or 咸>1
醋 (0 0)
盐 (0 1)
HanakoのPlayground☄️
Photo
^1笑点解析
#linux rootfs容器化的潮流下,
apt-get; systemd 做梦也没想到有人把挂载当cp 和pm2用 😅,效仿 apk, Magisk 了,直接放弃文件读写
(其实就是OS提供给 read,write,readdir 函数的 hook ,archlinux 一直是如此做 PKGBUILD ,玩得这么花 不就是为了免
天下苦OS碎片化久矣
^2 #plt #learn
CPS变换=ES6无栈协程=自动传回调
OS调度器=有栈C语言协程=单核多任务 免回调
#haskell Cont等Monad能够和do管道链配合,实现异步、伪随机等其他语言里稀松平常的「纯函数」特技
'入'演算里,CPS变换必须不直接求值,改接受回调k ,这样它们就能跨线程组装返回值. 用人话翻译下:
他的本意,应该是指CPS 就像
第三行的定义,按 Reactjs,Rx 都是CPS框架,只是不止能"return" 一次。 这个和指针关系不大,比如swap(a,b)是宏能定义的语法糖
^3
玩梗是很有新意,但希望各位订户记住: “高尚的是人,不是职业”
聪明的是人,不是编程语言;解决问题的是范式,不是语法规范
^4
推荐云风的Unity 3D教程。 u3d和GL使用左手y-up座标系,而不是数学的z-up
这利于2.5D横版游戏的开发
但z是“近大远小”的,和DOM翻转y理由一样。另外 cv2 是使用 bmp[y,x] 坐标,好奇怪的语法?
#linux rootfs容器化的潮流下,
$ mount 跟不上时代了apt-get; systemd 做梦也没想到有人把挂载当cp 和pm2用 😅,效仿 apk, Magisk 了,直接放弃文件读写
(其实就是OS提供给 read,write,readdir 函数的 hook ,archlinux 一直是如此做 PKGBUILD ,玩得这么花 不就是为了免
systemctl start 免分渠道发包呗天下苦OS碎片化久矣
^2 #plt #learn
CPS变换=ES6无栈协程=自动传回调
OS调度器=有栈C语言协程=单核多任务 免回调
#haskell Cont等Monad能够和do管道链配合,实现异步、伪随机等其他语言里稀松平常的「纯函数」特技
'入'演算里,CPS变换必须不直接求值,改接受回调k ,这样它们就能跨线程组装返回值. 用人话翻译下:
(x): k=>x(k) # 常量或sleep(1s) (k),read 等基元第二行就很抽象,「拿到流水线需要的东西」就是CPS,那么调用栈-返回也是CPS了, 但那是编程的基础课
(x=>A): k=>k (x=>A)
A(Arg): k=>A (K=>K(Arg))(k) #大K用来求值参数,小k回调
他的本意,应该是指CPS 就像
fetch(://fn/args).then(ret=> 可以实现RPC。 kt 的 withContext(Dispatcher.IO) 和 "use server" 就是这个意思,可惜这术语也说了,用处却列不清楚第三行的定义,按 Reactjs,Rx 都是CPS框架,只是不止能"return" 一次。 这个和指针关系不大,比如swap(a,b)是宏能定义的语法糖
^3
玩梗是很有新意,但希望各位订户记住: “高尚的是人,不是职业”
聪明的是人,不是编程语言;解决问题的是范式,不是语法规范
^4
推荐云风的Unity 3D教程。 u3d和GL使用左手y-up座标系,而不是数学的z-up
这利于2.5D横版游戏的开发
但z是“近大远小”的,和DOM翻转y理由一样。另外 cv2 是使用 bmp[y,x] 坐标,好奇怪的语法?
❤1
#algorithm 动态规划 dynamic programming
eg. lcs公共子串, knapack背包最佳配重, edit-distance编辑距离
http://www.bilibili.com/video/BV1FJ4m1M7RJ
🌚这是连“二参记忆化递归”这个常识都没说出来啊。
其实DP的经典案例是fib f x=x if x<2 else f(x-1)+f(x-2)
转化为一维的 f=[0,0]; f.i=f.(i-1)+f.(i-2)
这样,动归比递归的主要难度,是确立基线,以及用抽象的2D数组.ij取代fib()思考「状态转移方程」
二者的相似,好比「积分傅里叶变换」与离散DFT
动归比 @memo def fib 只优化了常量时间
fib只需要朝0的方向计算两个子问题,可以 iter 优化
实现上,如 lcs
f(,)=0
f(a,ab)=1
f(b,ab)=1
来,可视化一个corner case!
\ a b
a 1 0
b 0 2
可见,参数网格
晦涩就对了,因为它等效这个:
lcs([A],[B])=A==B
lcs([A,a],[B,b])=
max(lcs(a,b) lcs(b,a))+(A==B? 1:0)
lcs([A,a],[B])= lcs(a)+(A==B? 1:0)
+2分支
f(a,ab)=1 直接匹配AaBb +1
f(b,ab)=1 则匹配ABb,居然也与表格等效
更易懂了吗?见仁见智,尤其是 (a,b)(b,a)是干啥? 当然,因为lcs本身就有交换律啊..
不过我要指出,这个语法是有问题的(尽管 #haskell 在红黑树/快排上比cpp可易读不止一半),如果专门对DP设计,一定有 np.einsum 那样更优雅的表述
DP教学总是涉及表格[i,j] 而可视化并不方便,不过解「编辑距离」,你总得先学diff(abc, aBc)=[1: -1+B] 怎么计算吧
* https://t.me/dsuses/5335 基于memo的lcs,人不如AI
* https://t.me/dsuse/18877?single fib流
只能先留个坑🌝触摸屏累死了
eg. lcs公共子串, knapack背包最佳配重, edit-distance编辑距离
http://www.bilibili.com/video/BV1FJ4m1M7RJ
🌚这是连“二参记忆化递归”这个常识都没说出来啊。
其实DP的经典案例是fib f x=x if x<2 else f(x-1)+f(x-2)
转化为一维的 f=[0,0]; f.i=f.(i-1)+f.(i-2)
这样,动归比递归的主要难度,是确立基线,以及用抽象的2D数组.ij取代fib()思考「状态转移方程」
二者的相似,好比「积分傅里叶变换」与离散DFT
动归比 @memo def fib 只优化了常量时间
fib只需要朝0的方向计算两个子问题,可以 iter 优化
实现上,如 lcs
f(,)=0
f(a,ab)=1
f(b,ab)=1
来,可视化一个corner case!
\ a b
a 1 0
b 0 2
可见,参数网格
r.(0 0)= A0==B0
r.(i j)= (Ai==Bj? 1:0)+
try max( r.(i-1) r.(j-1))
i==0: r.(0 j-1) #第0行没法再减
j==0: r.(i-1 0)
晦涩就对了,因为它等效这个:
lcs([A],[B])=A==B
lcs([A,a],[B,b])=
max(lcs(a,b) lcs(b,a))+(A==B? 1:0)
lcs([A,a],[B])= lcs(a)+(A==B? 1:0)
+2分支
f(a,ab)=1 直接匹配AaBb +1
f(b,ab)=1 则匹配ABb,居然也与表格等效
更易懂了吗?见仁见智,尤其是 (a,b)(b,a)是干啥? 当然,因为lcs本身就有交换律啊..
不过我要指出,这个语法是有问题的(尽管 #haskell 在红黑树/快排上比cpp可易读不止一半),如果专门对DP设计,一定有 np.einsum 那样更优雅的表述
DP教学总是涉及表格[i,j] 而可视化并不方便,不过解「编辑距离」,你总得先学diff(abc, aBc)=[1: -1+B] 怎么计算吧
* https://t.me/dsuses/5335 基于memo的lcs,人不如AI
* https://t.me/dsuse/18877?single fib流
只能先留个坑🌝触摸屏累死了
Bilibili
ACM 金牌选手教你动态规划的本质。力扣 No.72 编辑距离,真·动画教编程,适合语言初学者或编程新人。_哔哩哔哩_bilibili
虽然我没有写出来,但是你应该知道我要什么吧 XD, 视频播放量 33101、弹幕量 168、点赞数 1694、投硬币枚数 699、收藏人数 2134、转发人数 118, 视频作者 NotOnlySuccess, 作者简介 大一接触编程 2 次晋级 World Finals 并获最快解题奖;4 次 ACM亚洲区金牌;浙江省赛冠军;曾就职阿里、谷歌、微信,相关视频:边睡边学算法丨第一期,动态规划入门50题,10分钟彻底搞懂“动态规划”算法,【动态规划专题班】ACM总冠军、清华+斯坦福大神带你入门动态规划算法,Leetcode力扣…
https://ray-eldath.me/programming/three-important-ideas/ #statement #PLT
Ray: 我的一位朋友如此评论这些文章:他说真正理解抽象的唯一方式是通过抽象本身,而不是通过并不准确的类比。
「为了充分地("有用处"地)学习这些抽象,你必须去学数学,而不是通过一些糊里糊涂的文章,它们除了类比还是类比」
Dr: 要找到 “哪些代码遵循此抽象” 并不是必须的。像 Monad 这样的概念是非常抽象和通用的:它是一个描述了一套广泛的编程模式、数据结构、库和 API 的概念,其强大之处在于它们,是对如何设计和使用这种抽象的指导原则
成为高效的程序员并不需要理解全部的联系。其他人自然会强烈反对 :-)
Ray: "可仅仅知道 JavaScript 里的 Promise 本质上是 Monad,而 Functor “又是一个盒子” 并不能帮助你成为更好地程序员,而在你自己的库中使用这些词语只会让你的下游觉得不舒服"
...概念是非常抽象和通用的
等等,
逻辑学告诉我,抽象是通用的反面,就像社会是丛林的反面: 您的知识若是通用的,它一定能与各应用领域紧密联系,学生如何觉得它抽象!?
人们就是讨厌空谈,所以设计各种简洁而通用的API;人民就是讨厌丛林,所以组织出了社会。 现在竟有人觉得存在通用抽象和丛林社会?? 这就像是说javadoc等机翻的玩意,比各种demo, test甚至产品更能展示项目的价值!
“靠代码行数来衡量软件功能的进度,就像是凭零件重量来衡量飞机制造的进度——Gates
靠知识点的难度衡量价值,同理。😅
我看这种观念者,确实是够抽象。 “类比”的本质是抽象,而「学习」只是用自己领域的思维,去「组合」「代换」出别人说的那种东西,带给你工具和知识图谱上的价值。
大家来到世上都是白痴,没有谁拥有无法被代换给别人的知识「原子」。写文爱用「未定义就使用的概念」,想设计新的抽象,却不谈理由?那就是 lier
无论我们用什么领域的「原子」组合出了CS的知识点,那都是潜在的价值,为空洞的术语绑定了新的语意,更摒弃了其中被“凭空捏造”的哲学。让IT人说话,天塌不下来!
难者,不会也。作者已死,凭什么说的道你就是「高等抽象」,大家能跟上的就是"糊里糊涂"的抽象? Einstein, Feynman 说物理的最前沿应该教高中生搞懂,难道你们的方法论比爱翁还科学?
人们给车设计的引擎,能达到原理极限的98%,那么 #CS 对IT的指导,除了让空指针反复造成十亿美元bug,就是让功能不变的软件,随着更新越来越臃肿和慢? 这算什么CS!
Monad很图论,很优雅啊! 但它能从
数学的精度是无限的、数学的等号是有交换律的。 #Haskell 里有模式匹配,但有"Var(x)作为值"吗?
只要变量是值,模式匹配、类型推理、响应式,甚至函数的编译,就真是小孩都会写的栏大街了。让
FP们连正反函数如
https://arendjr.nl/blog/2024/07/post-architecture-premature-abstraction-is-the-root-of-all-evil/#:~:text=achieved%20through%20a%20much%20simpler%20function
“个体的经历,不过是一个庞大的(形式主义)系统下极其表面化的闪烁而已”
可那个闪烁对某一天的用户来说就是一切。 自然原理,亘古不变。如果只是发现他们就能改变世界? 工业革命可以提早数百年。
人一思考,上帝就发笑。 你们的抽象,永远概括不了现实的领域、具体的人所提供独有的组合与可能性。
知其变,守其恒,为天下式?
穷其变,悟不穷,以明我志!
不能为每个人产生普世价值,是理论的悲哀。
Ray: 我的一位朋友如此评论这些文章:他说真正理解抽象的唯一方式是通过抽象本身,而不是通过并不准确的类比。
「为了充分地("有用处"地)学习这些抽象,你必须去学数学,而不是通过一些糊里糊涂的文章,它们除了类比还是类比」
Dr: 要找到 “哪些代码遵循此抽象” 并不是必须的。像 Monad 这样的概念是非常抽象和通用的:它是一个描述了一套广泛的编程模式、数据结构、库和 API 的概念,其强大之处在于它们,是对如何设计和使用这种抽象的指导原则
成为高效的程序员并不需要理解全部的联系。其他人自然会强烈反对 :-)
Ray: "可仅仅知道 JavaScript 里的 Promise 本质上是 Monad,而 Functor “又是一个盒子” 并不能帮助你成为更好地程序员,而在你自己的库中使用这些词语只会让你的下游觉得不舒服"
...概念是非常抽象和通用的
等等,
逻辑学告诉我,抽象是通用的反面,就像社会是丛林的反面: 您的知识若是通用的,它一定能与各应用领域紧密联系,学生如何觉得它抽象!?
人们就是讨厌空谈,所以设计各种简洁而通用的API;人民就是讨厌丛林,所以组织出了社会。 现在竟有人觉得存在通用抽象和丛林社会?? 这就像是说javadoc等机翻的玩意,比各种demo, test甚至产品更能展示项目的价值!
“靠代码行数来衡量软件功能的进度,就像是凭零件重量来衡量飞机制造的进度——Gates
靠知识点的难度衡量价值,同理。😅
我看这种观念者,确实是够抽象。 “类比”的本质是抽象,而「学习」只是用自己领域的思维,去「组合」「代换」出别人说的那种东西,带给你工具和知识图谱上的价值。
大家来到世上都是白痴,没有谁拥有无法被代换给别人的知识「原子」。写文爱用「未定义就使用的概念」,想设计新的抽象,却不谈理由?那就是 lier
无论我们用什么领域的「原子」组合出了CS的知识点,那都是潜在的价值,为空洞的术语绑定了新的语意,更摒弃了其中被“凭空捏造”的哲学。让IT人说话,天塌不下来!
难者,不会也。作者已死,凭什么说的道你就是「高等抽象」,大家能跟上的就是"糊里糊涂"的抽象? Einstein, Feynman 说物理的最前沿应该教高中生搞懂,难道你们的方法论比爱翁还科学?
人们给车设计的引擎,能达到原理极限的98%,那么 #CS 对IT的指导,除了让空指针反复造成十亿美元bug,就是让功能不变的软件,随着更新越来越臃肿和慢? 这算什么CS!
Monad很图论,很优雅啊! 但它能从
x+1==2 得出x=1吗? does it run backwards?数学的精度是无限的、数学的等号是有交换律的。 #Haskell 里有模式匹配,但有"Var(x)作为值"吗?
只要变量是值,模式匹配、类型推理、响应式,甚至函数的编译,就真是小孩都会写的栏大街了。让
let(['x','y'],a) 生成 x=a[0],y=a[1] 谁不会啊 也配叫语言特性FP们连正反函数如
show-out(1, "1"); show(res, "1"); res==1 都没建模,无精度int、向量、矩阵和微分都不如numpy sympy,也好意思谈数学性? 起码把Fortran的矩阵搞明白再说吧https://arendjr.nl/blog/2024/07/post-architecture-premature-abstraction-is-the-root-of-all-evil/#:~:text=achieved%20through%20a%20much%20simpler%20function
费曼家有一套《大英百科全书》,父亲常让费曼坐在他的膝上,给他念里边的章节。
有一次念到恐龙,书里说,“恐龙的身高有 25 英尺, 头有 6 英尺宽。” 父亲停顿了念书, 对费曼说,
“唔,让我们想一下这是什么意思。这也就是说,要是恐龙站在门前的院子里,那么它的身高足以使它的脑袋凑着咱们这两层楼的窗户,可它的脑袋却伸不进窗户,因为它比窗户还宽呢!”
就是这样, 他总是把所教的概念变成可触可摸, 有实际意义的东西。
“个体的经历,不过是一个庞大的(形式主义)系统下极其表面化的闪烁而已”
可那个闪烁对某一天的用户来说就是一切。 自然原理,亘古不变。如果只是发现他们就能改变世界? 工业革命可以提早数百年。
人一思考,上帝就发笑。 你们的抽象,永远概括不了现实的领域、具体的人所提供独有的组合与可能性。
知其变,守其恒,为天下式?
穷其变,悟不穷,以明我志!
不能为每个人产生普世价值,是理论的悲哀。
Ray Eldath's Blog
计算机领域的三个重要思想:抽象,分层和高阶
昨晚看了点比较有意思的东西,于是决定写一篇文章简单讲一下。 本文致力于概括我对计算机界三个重要思想的体会和认识。我希望做的并不是简单的百科全书式的列举(“A 体现了抽象思想;B 体现了分层思想…”),而是从这些思想中选取几个我个人较有体会(或者是我单纯觉得十分有趣)的侧面拿来细讲。这些侧面仅仅能覆盖这些思想应用范围中十分微小的一部分,它们并不是最有代表性的、亦非最为重要的——仅仅因为,我个人对这点
duangsuse::Echo
https://ray-eldath.me/programming/three-important-ideas/ #statement #PLT Ray: 我的一位朋友如此评论这些文章:他说真正理解抽象的唯一方式是通过抽象本身,而不是通过并不准确的类比。 「为了充分地("有用处"地)学习这些抽象,你必须去学数学,而不是通过一些糊里糊涂的文章,它们除了类比还是类比」 Dr: 要找到 “哪些代码遵循此抽象” 并不是必须的。像 Monad 这样的概念是非常抽象和通用的:它是一个描述了一套广泛的编程模式、数据结构、库和…
#PLT #learn 文中提到一个Futa对应关系(第一类二村映射 first Futamura projection), 展开讲讲还蛮有趣 🙉
首先,js人都会写计算器的,和 echo input/server 一样的难度
这被称为「前缀式polish notation」,是Lisp的国是
教个小诀窍:js里函数只是一种字面常量,可以被for生成
为了更像“语言”,可以用列表(进一步就是"1+2"的代码)实现语法,便于糖化
Monadic递归下降是这样AbBc的,因为不能右移流指针。不如逆波兰好看!不过这种程度的递归可以学学。
再看下「解释器interpreter」比计算器强在哪: 能够“在调用时传值”,也就是要有 argument[0] 这种“环境变量”
很厉害!现在有常数外的“语法”了,有变量了,高阶了!或许你需要学动态作用域(原型链?)、调用栈call-ret、惰性求值如&&|| blabla,还有深不可测的编译优化呢!
不就加一个箭头么。
env被称为运行时,它可以是JVM, import dis 或者别的bytecode解释器,这能减少tree-walk对递归栈的频繁依赖
这种
编译器并不需要与DSL这些技巧隔离,如果我们把 env=>x 写作 JSON(x) 而 env=>env[i] 写作$i ,既
以这种人类或机器可读的结构序列化一些函数被"bind"到的lit,就得到了对应的代码。jvm的 lconst 1, aload_0 this参数, iadd (2->1)甚至是自动分配参数寄存器的!
https://www.yinwang.org/blog-cn/2014/01/04/authority#:~:text=partial%20evaluator。其实并不是特别神奇的东西,只需要在普通解释器里面改一两行代码就行,可是有人
二段求值。代码是流动的数据,内存是暂停的程序。本文甚至比赢王更直观: https://www.yinwang.org/blog-cn/2012/08/01/interpreter
定义个“元循环加法”吧?
如果你乐意,还可以支持基于全局表的递归 fib=f(x)=x<2? 1 : f(x-1)+f(x-2)
这一切都不会突破以上的定义框架。 If 不会,Call(f,x-1) 不会.. 这就是java的反射嘛。
我不想再写什么了。 我看过许多“编译原理书”,他们连这个=>都写不明白。 更何谈用Visitor实现(反)序列化 这些既理论又实践的approach
#haskell
这类基于list或class{子类多型} 的
> 高阶的思想或许是本文的所有思想中最为重要的那一个 https://ray-eldath.me/programming/three-important-ideas/
你们知道函数的函数、类型的类型、语言的语法,却难以创造「语言中的语言」—跨越用途与界限而一致的语意。
我看numpy和 taichi-lang.org 就用的很好,比LLVM好一个世代
顺带一提,上文还使用了 Tagless Final 和 De Bruijn 索引 😇 。只是…… 你甚至不需要知道它们的名字。
说到底,元编程也只是编程。就像“学会学习”只是一种策略,它对学习而言,并非例外情况。难者,不会也,譬如在谈"bind(x=1)后函数值字面是否改变"时提起「颗粒化curryArg」 当然会让人迷糊
node,graal会编译js,jvm代码(这也是为何Chrome页偶尔会segfault); JVM会使用汇编模板+JIT 的混合式优化,Cython则把“甜妹语言”翻译到C,LLVM则是个伪装成NodeGraph虚拟机的codegen
如果只用"是否该调用javac"来区分语言,认为C类型总比python更快的话,你会发现程序员钟意的都是freak!
宏,macro码可揉,是传入与生成class{}等字面的函数。和+-*/一样只是函数,而函数、类型,只是值,别被“静态分析”骗了,它们送你的class只是病态类型。
入,lambda栏目答,看x,A,B等栏目回答的算式
这点篇幅放前言都觉得寒碜吧? 可就是没人做到啊。 扶她投影?不知道的还以为是HP projector 的娘化版呢。。
PLT能对dev点化到的可谓寥寥, 但100%都是必要且急需的知识和"抽象",像Prolog的模板与响应式html编程("FRP") 什么的。 Monadic错误处理我不想吐槽什么,只能说不怕不识货,只怕和Kotlin比!
跑题了。当然,聪明的你会发现Fun的返回违背了"基线指明了递归的类型"这一原则,没生成 env=>
那C语言函数指针是这样的,env由*rbp[0:2]=[retRbp,retAddr]这些CPU变量提供,但JS里的闭包可以从env偷变量(Var作为值,mut ref),所以说「闭包是穷人的(单方法)对象」
C里还有"函数符号",那是由ld.so实现的全局表,让.o对象能先被mmap到任意地址,再去回填calls
真正的那个全局表叫 $PATH: ,以及没定义main的对象的 $LD_LIBRARY_PATH 。
首先,js人都会写计算器的,和 echo input/server 一样的难度
1+2*3
十(1, X(2,3)) -7
十=(A,B)=>A+B
这被称为「前缀式polish notation」,是Lisp的国是
教个小诀窍:js里函数只是一种字面常量,可以被for生成
Object.entries("十一Xノ").forEach(([i,x])=>
this[x]=eval(`(A,B)=>A${"+-*/"[i]}B`))为了更像“语言”,可以用列表(进一步就是"1+2"的代码)实现语法,便于糖化
Eval([十,1, X,2,3]) -7
Eval=([f,...x])=>(!f.call)? [f,x] : //基线返回两项,递归就都是两项
(([A,b]=Eval(x),[B,c]=Eval(b))=>[f(A,B), c] )()
Monadic递归下降是这样AbBc的,因为不能右移流指针。不如逆波兰好看!不过这种程度的递归可以学学。
再看下「解释器interpreter」比计算器强在哪: 能够“在调用时传值”,也就是要有 argument[0] 这种“环境变量”
很厉害!现在有常数外的“语法”了,有变量了,高阶了!或许你需要学动态作用域(原型链?)、调用栈call-ret、惰性求值如&&|| blabla,还有深不可测的编译优化呢!
不就加一个箭头么。
十=(A,B)=> (env=>A(env)+B(env)) //现在知道为啥该用for生成函数?
Lit=x=> env=>x
Arg=i=> env=>env[i] //PHP写作 $x, 模仿bash的$1~$*
Fun=(n,f)=>f(Array(n).fill(0).map((x,i)=> Arg(i)))
env被称为运行时,它可以是JVM, import dis 或者别的bytecode解释器,这能减少tree-walk对递归栈的频繁依赖
这种
formSytanx.bind(consts=lit/arg/global/Types..) 的"部分传参"函数,称为编译器,而它的返回就是classFile等类型。 编译器并不需要与DSL这些技巧隔离,如果我们把 env=>x 写作 JSON(x) 而 env=>env[i] 写作$i ,既
Lit=x=> gcc? CBor.dumps(x) : (env=>x)以这种人类或机器可读的结构序列化一些函数被"bind"到的lit,就得到了对应的代码。jvm的 lconst 1, aload_0 this参数, iadd (2->1)甚至是自动分配参数寄存器的!
https://www.yinwang.org/blog-cn/2014/01/04/authority#:~:text=partial%20evaluator。其实并不是特别神奇的东西,只需要在普通解释器里面改一两行代码就行,可是有人
二段求值。代码是流动的数据,内存是暂停的程序。本文甚至比赢王更直观: https://www.yinwang.org/blog-cn/2012/08/01/interpreter
定义个“元循环加法”吧?
十1=Fun(1, ([A])=>十(A, Lit(1)))
十1([232]) -233
//[Lit(f),x] 补丁下Eval"解析器"。计算器、解释器、编译器间,其实并非泾渭分明
Eval([十,1, X,2,X,1,3])[0]([2]) -7
如果你乐意,还可以支持基于全局表的递归 fib=f(x)=x<2? 1 : f(x-1)+f(x-2)
这一切都不会突破以上的定义框架。 If 不会,Call(f,x-1) 不会.. 这就是java的反射嘛。
我不想再写什么了。 我看过许多“编译原理书”,他们连这个=>都写不明白。 更何谈用Visitor实现(反)序列化 这些既理论又实践的approach
#haskell
data Val=L Int|Op Char Val Val deriving(Show)
(Op '+' (L 1) (Op '*' (L 2) (L 3) ))这类基于list或class{子类多型} 的
Eval(e=^^, env={proto:{..}}),与本文使用的闭包法是等价的。「闭包是穷人的对象」,Promise 给它暴露到了 obj.then(Continuation)> 高阶的思想或许是本文的所有思想中最为重要的那一个 https://ray-eldath.me/programming/three-important-ideas/
你们知道函数的函数、类型的类型、语言的语法,却难以创造「语言中的语言」—跨越用途与界限而一致的语意。
我看numpy和 taichi-lang.org 就用的很好,比LLVM好一个世代
顺带一提,上文还使用了 Tagless Final 和 De Bruijn 索引 😇 。只是…… 你甚至不需要知道它们的名字。
([A])=> 经常被实现为KV["A"],但点明它的本质,却比写解释器更简单!说到底,元编程也只是编程。就像“学会学习”只是一种策略,它对学习而言,并非例外情况。难者,不会也,譬如在谈"bind(x=1)后函数值字面是否改变"时提起「颗粒化curryArg」 当然会让人迷糊
node,graal会编译js,jvm代码(这也是为何Chrome页偶尔会segfault); JVM会使用汇编模板+JIT 的混合式优化,Cython则把“甜妹语言”翻译到C,LLVM则是个伪装成NodeGraph虚拟机的codegen
如果只用"是否该调用javac"来区分语言,认为C类型总比python更快的话,你会发现程序员钟意的都是freak!
宏,macro码可揉,是传入与生成class{}等字面的函数。和+-*/一样只是函数,而函数、类型,只是值,别被“静态分析”骗了,它们送你的class只是病态类型。
入,lambda栏目答,看x,A,B等栏目回答的算式
这点篇幅放前言都觉得寒碜吧? 可就是没人做到啊。 扶她投影?不知道的还以为是HP projector 的娘化版呢。。
PLT能对dev点化到的可谓寥寥, 但100%都是必要且急需的知识和"抽象",像Prolog的模板与响应式html编程("FRP") 什么的。 Monadic错误处理我不想吐槽什么,只能说不怕不识货,只怕和Kotlin比!
跑题了。当然,聪明的你会发现Fun的返回违背了"基线指明了递归的类型"这一原则,没生成 env=>
那C语言函数指针是这样的,env由*rbp[0:2]=[retRbp,retAddr]这些CPU变量提供,但JS里的闭包可以从env偷变量(Var作为值,mut ref),所以说「闭包是穷人的(单方法)对象」
C里还有"函数符号",那是由ld.so实现的全局表,让.o对象能先被mmap到任意地址,再去回填calls
真正的那个全局表叫 $PATH: ,以及没定义main的对象的 $LD_LIBRARY_PATH 。
Telegram
duangsuse::Echo
#plt #code 谈到RPN,我昨天有个灵感想把js做成缩进的形式
就回顾了优先级算法(嵌套深度包括h1~h6树本质上也是优先级)
示例算法 sexp(tok('1 (2 3) 4')), exp(tok('1*2+3'), optab)
optab={[';']:-1}
"=;+ -;* /".split(';').map((a,l)=>a.split(' ').forEach(x=> optab[x]=l ))
exp=(s/*token x单项o算符 x..*/, l/*evels 大则深…
就回顾了优先级算法(嵌套深度包括h1~h6树本质上也是优先级)
示例算法 sexp(tok('1 (2 3) 4')), exp(tok('1*2+3'), optab)
optab={[';']:-1}
"=;+ -;* /".split(';').map((a,l)=>a.split(' ').forEach(x=> optab[x]=l ))
exp=(s/*token x单项o算符 x..*/, l/*evels 大则深…
duangsuse::Echo
#rust #go #algorithm UmbraString 是对带长度指针(py.bytes, rs.slice) 的(免链接inline),而 SwissTable 是对Hash预分组查表的免链接! 我们知道,Java 存在(装箱boxing) 一说,也就是int,char等字面值的堆分配 (这是泛型擦除 vs template<>化新建的编译期细节),因此JDK8用class Stream.OfInt{}缓解了reified泛型的缺失 那么,(拆箱unwrap) 其实就是在值的内存上内联,像C++栈分配。…
#algorithm #防自学 🤓 让我来示范一下怎么概括算法思路
要介绍的是在stdlib里,用于组织集合类、JSON的3个重要结构:
它们对各种app和其他算法性能效能的重要性,好比json(cbor.me)之于REST、zip之于jvm和pip。 因为是涉及SDK实现的内容,也主观评价下语法设计
下面用胖指针(x64上void*是8b, 胖指16b)、链表、快速排序简单的实现3者 #code
https://colobu.com/2023/06/29/replace-std-map-faster/chunk-index-memory.jpg
用例和 #haskell #code https://gist.github.com/duangsuse/26b80c39e1d8f7549b9cf244d8de1ce4
题外话,闭包值和 x.interface_cast(T) 的双指针&dyn 随结构传入T的函数表
UmbraString 和上文Str{n,buf}胖指针一样是16b,但它的.n和jvm一样只寻址[4b:int]的长度,其后最少随4b的免链接char 用于比大小
对于n>12的buf,剩下8b换成指针,指针的高2位用于标记GC信息: 持久pin、临时ref、用后即焚val
很明显!这是一种灵活利用了x86内存布局的b"length"实现,和x32压缩指针一样,节省了sort解指针的时间
SwissTable 是对Hash预分组查表的免链接。我们知道, dict/HashMap/lua.Table 这样的{K:V, K1:V} 单映射常被用于查找和缓存,在C++里更是会区分 unordered_map, rb_map(以排序radix,SortedSet,而非hash作为预分组线索)
它的最简实现是lisp里的链表
即便8b hashCode,也是一定会冲突的,哪怕是int.hash也会因 buck[hash%nBuck] 有限而退化为线性查找,负载因子(kv/nBuck 过大),这时就要扩容。Go的扩容基于一种空间换时间的优化(图1, 为了减少求余数的冲突,除数都会采用2的指数)
扩容后的冲突集,可以用链表(UnionFind)或数组(slot), 从那往右找
Swiss 更聪明,它对每slot对应1b的元数据,最高位0x80=无效项 ,0xFF=null结尾 ,低7位用于存储hashcode的高7位,这么摘要是为了SIMD128bit 1次对比8个KV
不仅仅只是CPU CACHE友好,这样的结构配合原子操作,相信很容易做出一个并发版本的hash table
快速求余(x,n)= uint32((uint64(x) * uint64(n)) >> 32)
#dalao https://init.blog/fast-newton-sqrt/
最近的一种基于partition(区间而非idx)的快排也很有趣: less than pivotL | between pivotL and pivotR | greater than pivotR
要介绍的是在stdlib里,用于组织集合类、JSON的3个重要结构:
b"ytesPtr", {K:V}, sorted([0 2 1])=[0 1 2]它们对各种app和其他算法性能效能的重要性,好比json(cbor.me)之于REST、zip之于jvm和pip。 因为是涉及SDK实现的内容,也主观评价下语法设计
下面用胖指针(x64上void*是8b, 胖指16b)、链表、快速排序简单的实现3者 #code
#define Col(...) typedef struct{__VA_ARGS__;} T;
#define T Str
Col(size_t n; char* buf) //C的类型本应默认为指针, *胖指针,像kt那样对Int等类型免链接化。简洁的UNIX里,Type* 快成为public那样的形式主义啦
#define T_Link(E,T) struct T{E x; T* xs;}
T_Link(int,Nums) //template<T> 允许类型推理,即一致化调用和返回处的<T>。可怜gcc/clang无论对宏还是模板的报错皆如内容农场,不具有可读性https://colobu.com/2023/06/29/replace-std-map-faster/chunk-index-memory.jpg
用例和 #haskell #code https://gist.github.com/duangsuse/26b80c39e1d8f7549b9cf244d8de1ce4
题外话,闭包值和 x.interface_cast(T) 的双指针&dyn 随结构传入T的函数表
qsort :: (Ord a) => [a] -> [a]
qsort [] = []
qsort (x:xs) =
let smallerSorted = qsort [a | a <- xs, a <= x]
largerSorted = qsort [a | a <- xs, a > x]
in smallerSorted ++ [x] ++ largerSorted
UmbraString 和上文Str{n,buf}胖指针一样是16b,但它的.n和jvm一样只寻址[4b:int]的长度,其后最少随4b的免链接char 用于比大小
对于n>12的buf,剩下8b换成指针,指针的高2位用于标记GC信息: 持久pin、临时ref、用后即焚val
很明显!这是一种灵活利用了x86内存布局的b"length"实现,和x32压缩指针一样,节省了sort解指针的时间
SwissTable 是对Hash预分组查表的免链接。我们知道, dict/HashMap/lua.Table 这样的{K:V, K1:V} 单映射常被用于查找和缓存,在C++里更是会区分 unordered_map, rb_map(以排序radix,SortedSet,而非hash作为预分组线索)
它的最简实现是lisp里的链表
T_Link(struct {int A,B;}, int_LnKV) :没留任何线索来减枝!即便8b hashCode,也是一定会冲突的,哪怕是int.hash也会因 buck[hash%nBuck] 有限而退化为线性查找,负载因子(kv/nBuck 过大),这时就要扩容。Go的扩容基于一种空间换时间的优化(图1, 为了减少求余数的冲突,除数都会采用2的指数)
扩容后的冲突集,可以用链表(UnionFind)或数组(slot), 从那往右找
Swiss 更聪明,它对每slot对应1b的元数据,最高位0x80=无效项 ,0xFF=null结尾 ,低7位用于存储hashcode的高7位,这么摘要是为了SIMD128bit 1次对比8个KV
不仅仅只是CPU CACHE友好,这样的结构配合原子操作,相信很容易做出一个并发版本的hash table
快速求余(x,n)= uint32((uint64(x) * uint64(n)) >> 32)
#dalao https://init.blog/fast-newton-sqrt/
float InvSqrt(float x) {
float xhalf = 0.5f*x;
int i = *(int*)&x;
i = 0x5f3759df - (i >> 1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}最近的一种基于partition(区间而非idx)的快排也很有趣: less than pivotL | between pivotL and pivotR | greater than pivotR
duangsuse::Echo
作为近两年开始流行的新语言特性,Unison, 仓颉 都支持了效应捕获的语法
GitHub
cangjie_stdx/doc/libs_stdx/effect/effect_package_overview.md at a7c43d44381a995262ac93ba56dce66090613269 · cangjielanguage/cangjie_stdx
Contribute to cangjielanguage/cangjie_stdx development by creating an account on GitHub.
https://x.com/i/grok/share/ZBRA0vtJcBzGQpq9KQd56pJPu #FP #haskell
很好,以前ohos是滥竽充数,现在都会“妄自菲薄”了。 本来就和JDK没的比,新特性还要到字缝里去翻,不是 🤡么? Rust好歹是原神呢😅
让各位看看外行写导论会写出什么幺蛾子来: https://zhuanlan.zhihu.com/p/76158581
我以前就是看的这种,纯浪费时间么,还不如蛤为仓颉的客座沙龙
我决定不把Effect归化到我的编程语言里去,技术细节见 #ai探讨 https://chat.librechat.ai/share/tPbd15iaCJ9_DMQNy7KuW🤯
#ai对比 https://chat.librechat.ai/share/mEcMHpAc4j_v3HYn4fzwA
😒 Kimi 这次有用多了,重要的是便宜
很好,以前ohos是滥竽充数,现在都会“妄自菲薄”了。 本来就和JDK没的比,新特性还要到字缝里去翻,不是 🤡么? Rust好歹是原神呢
https://gitee.com/HW-PLLab/pllab_slides/raw/dev/WechatOfficialAccSlides/20221123-EffectHandlers从函数式编程到系统级语言-by-DanGhica.pdf#page=8
https://github.com/cangjielanguage/cangjie_stdx/blob/a7c43d44381a995262ac93ba56dce66090613269/doc/libs_stdx/effect/effect_package_overview.md
让各位看看外行写导论会写出什么幺蛾子来: https://zhuanlan.zhihu.com/p/76158581
我以前就是看的这种,纯浪费时间么,还不如蛤为仓颉的客座沙龙
我决定不把Effect归化到我的编程语言里去,技术细节见 #ai探讨 https://chat.librechat.ai/share/tPbd15iaCJ9_DMQNy7KuW
#ai对比 https://chat.librechat.ai/share/mEcMHpAc4j_v3HYn4fzwA
Algebraic Effects(AEff) 是最近纯函数式PFer的动向,用于替代各种Monad。这其实是PF对“易变现实”的很大让步了
就像不理解 “伪递归=改参跳到def开头重来”,PF非常在意“堆变量不纯,栈变量不纯,参数变就纯”的无聊形式,于是就有了 `{IO,State,Random} Monad` 这些“纯粹的参数”,组合出Monad jQuery式的调用链,就叫“do语句块”
`State(0).fmap(u=>u+1)` 其实就是 `Var(0).then(u=>u+1)` ,因为u+1那块可以调用另一个“广义Promise”,就有了 `flatMap(async(u)=>u+1)` ,这就是染色问题(没错,这正是 #Haskell Logo >>= !)
flatMap的语意在List上很怪异,但对await这种Effect是必要的,记住:这个“盒子”不能拿 Var(wtf).v ,它只能 Var(wtf).then(cps) ,那么cps返回Var就会扰乱后续链!当然Monad有一套“Algebra而惧吧”的文字游戏(比如foldMap),这里只提applicable。
与强行拿call回调串联顺序,在语意层去手写interpreter里“求值序语法”的单子相比,AEff 更是典中典。 不过我在这里,却是要欲抑先扬的赞同AEff的4个爽点。
我喜欢把 Algebraic Effects(AEff) 函数叫做“袋鼠效应器”。错误、系统调用、localStorage等“感受器”被她放在育儿袋外,代数跳出袋子,感受器就【择机】把它放回去:
- 异常的冒泡、重抛或替换,类似Rust受检的Result<T,Err>
- “请求局部”的可变量容器,就像隔离的 Arena,localStorage,Worker全局 ,这样PFer就不会受“参数变才纯”的限制
- 单例工具的参数注入,比如“进程”的pwd,fetch的登录头
- 异步(syscall)!求值栈转回调链表,才能够做 sleep() 和 evpoll/queue 等“内核态操作”
异常异步相耦合,“IO结果注入”和可变容器相耦合,可以说很优雅的确定了报错与IO请求的“范畴”。
袋鼠妈妈,就是evloop,那感受器自然是线程了!catch时她停止resume,并且waitUntil池空的话,进程自然退出。
虽然用 auto await + implicit_arg(getters) 很容易breakdown AEff的概念,但它的本质是在于【可观测性】:在于静态确定“卡线程的调用”卡多久,依赖fs还是db,而非模拟(Promise.catch)。
AEff实现“参数变才纯”时,就比Monad轻量了许多: 别忘了var.flatMap(Var内)可以有then以外的键。 不过这种文字游戏对practical是白费脑力。
上面说欲抑先扬,那表扬完该批评了:AEff 中看不中用!
相对于DOM/Java“漏洞百出”的IO限界和样式值注入,Monad还不严谨吗? 尽信书不如无书,尽受检不如不检。一个白屏是100%不会抛错的,这就是“干净”的价格。
我自己用我自己的异常和异步范式,实现那“4个爽点”
Result有另一种实现:所有值,包括0都是truthy。null,Error和NaN是falsey。只允许object键的值是null,而访问Error的任何键都会throw。
在这种模式下,为Result提供冒泡、捕获替换的最好方法,不是AEff的“自动await”,而是nulley链(?. 和 ??),比如 txt=readSync('_404')? ,再加上“异步函数名”的约定,比如 read?('_404')? ,这些可以预处理实现,缺点是没有AE的默认值注入那么“自由”
至于可变状态,我使用类似Hooks的 n=inHole(0); n1=n.As(x=>x+1); n.As(233); assertEq(n1.v, 234) ,这个模式显然兼容Promise,As就等效Thenable。
---
有人告诉我可以直接在AEff语言上实现 read()?错处和“函数名?异步”,从而在架构上利用AEff,业务上却使用nulley链。这忽视了“async是否天然就该标明”的争议点。
AEff,或许是对系统调用的极佳抽象,也可以“插入”传统的OOP异常里。这就像closure变量不等于this变量,函数值与dict原型链,【正交】构成了对所有数据和操作的“Composer”。
但我认为,closure和SAM对象的协调性是特例,AEff和nulley链只能择一而从:AEff说到底还是 auto await + implicit_arg(getters) 的赠品,错处的一致性,不可能超过由链提供的默认值或rethrow操作!
AEff的赢面,是用函数签名【静态】确定线程的await,甚至能区分noreturn抛出vs捕获回调链表的优化,就像closure能unbox掉不泄露的SAM对象或纯const的cellvar。
但隐晦和省事,在工程上未必是好事,尤其是定义与使用处都不一致的AEff。
在应用编程时,几乎没人会根据Error子类去注入default,因为那天然是caller的职责,并且,其实 IO 是非常有限的!fetch() 或FileReader的caller“应该”有几层?
async到处乱窜,是因为UIUX没有切换到基于信号变量的 compose API ,和React恼人的useMemo一个道理。 本来是“setter续延”的粒度不够细导致的,AEff再去模糊界限,是对timing语意的忽视。
另一方面,AEff的函数签名过于复杂。异常和“系统调用”应该静态检查,但分的太细就是“屎上雕花”。我从没见过隔着两层caller还能针对性的fallback,而95%的App都以C的那种tag+msg处理异常。
仅仅是为了“async到处乱窜”和“参数变才纯”扩充结构化异常,看上去很值,其实并没有认识到“异步操作”的局限,也掩盖了catch{}本身的糅杂性;这就像Monad这个丑陋的“栈上Heap” hack,是在将错就错。Monad也是一个到处乱窜的“动态域”,do语句块可把它驯服了?
AEff 可以轻松实现“结构化并发”(协程的批量等待与撤回/close),看上去也很牛,但“异步的祖师”,DOM fetch() 用的却是信号变量,这不足以指出班门弄斧的“袋鼠笑话”吗?
ES6花了10年把你从for(x)带到forEach(x=>),“结构化”的学霸又把你带回去了,那你打算怎么用“结构化并发”写一个IDM的暂停重下呢?
DOM或许像没有兼容问题的一体机,但它的思想是纯粹的。我们需要的是 Worker<->Promise&Signal、普通计算<->异步IO管线 这样清晰的解耦,而不是半吊子的“大同”!
Please open Telegram to view this post
VIEW IN TELEGRAM
duangsuse::Echo
我决定不把Effect归化到我的编程语言里去,技术细节见
#PLT #learn HOAS/de Bruijn index 两种变量(和“栏目答”)实现方法 😅
#statement 知道我为啥圈选回复这条吗?
Algebraic Effects 也是一个PFP的“新秀”,但我考察后觉得没有吸纳的必要,那么不是新秀的deBruj啥,就更没有必要了。
我一直觉得提出它的领域(“定理证明器”)是多此一举的: 自己test都不明白,又何谈证明呢? 若非如此,何需证明呢?
不懂的证明没用,能懂的证明干嘛! 所以,我不会和AEff一样(因为像 #Haskell 还有些可取之处),就额外偏心一些
但是,这俩理论启发了我(作为负面教材),也算不虚此行,没有在弯来绕去的无聊符号上,浪费智商。
回头我会讲讲Prolog的“1=x式变量”与它所不能做的「形式化证明」有何联系。
https://t.me/dsuse/11841
#ai探讨 https://chat.librechat.ai/share/9iz2YegEZl9kEdiacDNmF
#statement 知道我为啥圈选回复这条吗?
Algebraic Effects 也是一个PFP的“新秀”,但我考察后觉得没有吸纳的必要,那么不是新秀的deBruj啥,就更没有必要了。
我一直觉得提出它的领域(“定理证明器”)是多此一举的: 自己test都不明白,又何谈证明呢? 若非如此,何需证明呢?
不懂的证明没用,能懂的证明干嘛! 所以,我不会和AEff一样(因为像 #Haskell 还有些可取之处),就额外偏心一些
但是,这俩理论启发了我(作为负面教材),也算不虚此行,没有在弯来绕去的无聊符号上,浪费智商。
回头我会讲讲Prolog的“1=x式变量”与它所不能做的「形式化证明」有何联系。
https://t.me/dsuse/11841
#ai探讨 https://chat.librechat.ai/share/9iz2YegEZl9kEdiacDNmF
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
duangsuse::Echo
前置知识:De Bruijn Index - Trebor的文章 - 知乎
https://zhuanlan.zhihu.com/p/94601499 #PLT #FP
https://zhuanlan.zhihu.com/p/94601499 #PLT #FP
duangsuse::Echo
#ai #dev 这位作者的效率观和我类似 https://t.me/hacker_news_zh/18607
但他觉得chatUI是最差的媒介,我是觉得只有聊天框适合("Zen Mode")。
如果我要维护代码基,“选取&重录”是好模式,但我习惯于连根拔起,忘掉还有旧代码,整个数据流拆的独立可用、解耦正交。
就像今天的消息里把 ascii art 写15遍的故事,我同样也把递归下降parserc在8种语言里写过>12遍,所以我并不抵制算法或API移植--我知道如何“控制变量”
而在ai建议上,我很少同步等待chat的回复,而是保持方向相同,始终领先1~3条消息, Fire-and-Forget(射后不管)的策略 🤪
我一般会隔离工作流,而不是通过工具模糊顺序和边界
另一方面,我有 bookmarklet/F12 帮忙把chat REPL化,而他可能不 习惯模糊“开发app与开发devtools”的边界,哪怕熟悉 #Haskell
主流觉得 Copilot 是正义,是因为主流开发者的日常工作是“隐式修补”(Patching);而我讨厌它,是因为我的核心工作是“净室推演”(Reasoning)。
另一方面,不打开心智开销重的IDE,我可以有“超线程workspaces”,正如 https://t.me/dsuse/21861 所言😒
这就是我的护城河(差异化): 无论小白vibe还是大神Claude,他们都是在臃肿的栈上,被隔离在“反射弧”之外,而我把实现和对 话串放在一个Loop&Notebook里-真正有思维的HITL😒
换句话说,我是计算密集型,虽然prompt很简单,却只有IO包给最先进的AI大哥😃
绝大部分dev是IO密集型,“回忆 API”、“查文档”或者“看懂之前的代码”已经突破舒适区了
如果是我,会直接假定API里会有我猜到的货,只需确定函数颜色(def/async/鉴权/class boilerplates ),然后“原谅比请求好”,就像
代码逻辑链已经在我脑子里跑通过了 (简单是可靠的先验, Top-down mood)。我不需要 Copilot 在旁边关于“能不能”猜测和二开 (那些复杂到不可靠都难读懂的实现)😒
当我看到"apksigner.jar"这个动词,我想到的其实只是一个被插入的bytes()或.RSA文件
当我看到apk这个名词,我想到的又是它里面每个文件的compile与安装流程、函数节点/XML,以及这些节点的极简调用demo。 从具体到抽象,从骨架到皮毛,就像庖丁解牛,我已经看不见代码基,看见的是它贫瘠到<200行的意图😅
我知道很多IT人以自己学到的术语和API为荣,90%的小白或大佬认为code是骨架,可在今天的我眼里,“用户的既视感”是架构,代码是皮毛。
其实这很“容易”: 我猜不出demo用法的就是烂代码。今天无论是前后端/算法/游戏,我写一个app往往就会自带一段框架,因为现在的框架太拉了。
#ai锐评 https://gemini.google.com/share/72fa6f07f978
但他觉得chatUI是最差的媒介,我是觉得只有聊天框适合("Zen Mode")。
如果我要维护代码基,“选取&重录”是好模式,但我习惯于连根拔起,忘掉还有旧代码,整个数据流拆的独立可用、解耦正交。
就像今天的消息里把 ascii art 写15遍的故事,我同样也把递归下降parserc在8种语言里写过>12遍,所以我并不抵制算法或API移植--我知道如何“控制变量”
而在ai建议上,我很少同步等待chat的回复,而是保持方向相同,始终领先1~3条消息, Fire-and-Forget
领先于LLM的建议是我眼里的轻松工作节奏,等待LLM帮忙续写,意味着我没用fast模型做好知识预备。
我一般会隔离工作流,而不是通过工具模糊顺序和边界
另一方面,我有 bookmarklet/F12 帮忙把chat REPL化,而他可能不 习惯模糊“开发app与开发devtools”的边界,哪怕熟悉 #Haskell
主流觉得 Copilot 是正义,是因为主流开发者的日常工作是“隐式修补”(Patching);而我讨厌它,是因为我的核心工作是“净室推演”(Reasoning)。
另一方面,不打开心智开销重的IDE,我可以有“超线程workspaces”,正如 https://t.me/dsuse/21861 所言
这就是我的护城河(差异化): 无论小白vibe还是大神Claude,他们都是在臃肿的栈上,被隔离在“反射弧”之外,而我把实现和对 话串放在一个Loop&Notebook里-真正有思维的HITL
只有“聊天框”才允许“遗忘”。如果堆砌(Stacking)代码,Copilot 当然才思敏捷,可我是在雕刻(Sculpting)逻辑,我不能看见自己不想看见的功能点。
我希望随时能忽略之前的所有实现,立刻切到另外的语言或 API set ,直接操纵数据流
换句话说,我是计算密集型,虽然prompt很简单,却只有IO包给最先进的AI大哥
绝大部分dev是IO密集型,“回忆 API”、“查文档”或者“看懂之前的代码”已经突破舒适区了
如果是我,会直接假定API里会有我猜到的货,只需确定函数颜色(def/async/鉴权/class boilerplates ),然后“原谅比请求好”,就像
可空?.链() 一样简单代码逻辑链已经在我脑子里跑通过了 (简单是可靠的先验, Top-down mood)。我不需要 Copilot 在旁边关于“能不能”猜测和二开 (那些复杂到不可靠都难读懂的实现)
不是“我去查查框架有什么”,而是“它应该有这个,如果没有,我就写个 Wrapper 让它变成这样”
如果一个库的用法复杂到让你记不住——那不是你的问题,是那个库设计太烂——这其实是DSL(领域特定语言)设计者的思维,再加上我要的可复用可插拔
所以,我的apksigner实现比“最佳实践”小200倍 ——它的存在是先于js代码的
https://github.com/patrickfav/uber-apk-signer/
当我看到"apksigner.jar"这个动词,我想到的其实只是一个被插入的bytes()或.RSA文件
当我看到apk这个名词,我想到的又是它里面每个文件的compile与安装流程、函数节点/XML,以及这些节点的极简调用demo。 从具体到抽象,从骨架到皮毛,就像庖丁解牛,我已经看不见代码基,看见的是它贫瘠到<200行的意图
我知道很多IT人以自己学到的术语和API为荣,90%的小白或大佬认为code是骨架,可在今天的我眼里,“用户的既视感”是架构,代码是皮毛。
其实这很“容易”: 我猜不出demo用法的就是烂代码。今天无论是前后端/算法/游戏,我写一个app往往就会自带一段框架,因为现在的框架太拉了。
#ai锐评 https://gemini.google.com/share/72fa6f07f978
Please open Telegram to view this post
VIEW IN TELEGRAM
春东西 dumb box
https://x.com/solanamummy/status/2044808122997284963?s=46
#PLT
我想吐槽 #Haskell 根生的可用性问题。f(1+1) 里int(2)的懒求值是蛤丝的“特点”,而 add=(\a \b a+b) 的强制“解耦合”是纯FP风俗,异曲同工
这是在制造“熵增”。
我们都知道 @autoclosure,即 f(1+1) 等效 f(lambda:1+1) 是Lazy<T>的必须(时间换空间,这是混淆求值阶段的代价),懒有什么好处呢?
允许自定义 if(Q,A,B) 和 and(A_or_break, B) “函数”?单子宏?这是在乱搞。
对于上面的“递等式”,显然有比 2==2 更高性能的证法,但符号的好处正如列表字典之于JSON——有得必有失。在Lisp继Fortran诞生后,这个【失】就是GC。
Thunk就是另一个“GC”,只是它屁用没有。GC至少能用“链表状图”帮你算出“字节数”状图在某天有多大,
Thunk(0参函数)知道“1天”是什么维度的值吗?
栏目答演算的本质是什么?字面量替换。这也是为什么ML99能用 #define (配合 if_##A##B 式模式匹配)的写出一个无编译期插件的DSL,所以无论是SKI、栈语言、Prolog(DFS)的替换都是同阶的,不存在哪种“血统”更高
Church老祖在写自然数 N=Z|N+1 和“神奇”的,不用struct{}写程序的组合子时,他只是把tuple放进了(*arg,)闭包里,通过.then(续延)的“链表线程栈”的方法,以及从arg里选续延。纯函数是另一种OOP编程而已。
所以“懒人”是和自己过不去:如果说 (add 0)=(\b 0+b) 无法被压缩(即更接近停机/靠近main的帧),是因为 b :: Int 值还有“极大”的潜在空间,无法在“Runtime”之前剪枝,(add 0 1) 或 0+1 凭什么不是字节(=0x1)呢?
Currying 是因为int(x)所示所有x的空间,4GB也装不下,Thunk,比如 Monad “解释器”和Haskell蛤丝引以为傲的 do(>>=) notation 的情况不同! 🤔💭
我想吐槽 #Haskell 根生的可用性问题。f(1+1) 里int(2)的懒求值是蛤丝的“特点”,而 add=(\a \b a+b) 的强制“解耦合”是纯FP风俗,异曲同工
这是在制造“熵增”。
我们都知道 @autoclosure,即 f(1+1) 等效 f(lambda:1+1) 是Lazy<T>的必须(时间换空间,这是混淆求值阶段的代价),懒有什么好处呢?
允许自定义 if(Q,A,B) 和 and(A_or_break, B) “函数”?单子宏?这是在乱搞。
((1)+1)==(1+1) 吗?哲学上来说,PL把自己当成“递等式”,过程和答案是等价且相等的。但“熵增”就是丢垃圾,蛤丝喜欢垃圾! 这是为啥呢?
对于上面的“递等式”,显然有比 2==2 更高性能的证法,但符号的好处正如列表字典之于JSON——有得必有失。在Lisp继Fortran诞生后,这个【失】就是GC。
Thunk就是另一个“GC”,只是它屁用没有。GC至少能用“链表状图”帮你算出“字节数”状图在某天有多大,
Thunk(0参函数)知道“1天”是什么维度的值吗?
栏目答演算的本质是什么?字面量替换。这也是为什么ML99能用 #define (配合 if_##A##B 式模式匹配)的写出一个无编译期插件的DSL,所以无论是SKI、栈语言、Prolog(DFS)的替换都是同阶的,不存在哪种“血统”更高
Church老祖在写自然数 N=Z|N+1 和“神奇”的,不用struct{}写程序的组合子时,他只是把tuple放进了(*arg,)闭包里,通过.then(续延)的“链表线程栈”的方法,以及从arg里选续延。纯函数是另一种OOP编程而已。
所以“懒人”是和自己过不去:如果说 (add 0)=(\b 0+b) 无法被压缩(即更接近停机/靠近main的帧),是因为 b :: Int 值还有“极大”的潜在空间,无法在“Runtime”之前剪枝,(add 0 1) 或 0+1 凭什么不是字节(=0x1)呢?
Currying 是因为int(x)所示所有x的空间,4GB也装不下,Thunk,比如 Monad “解释器”和Haskell蛤丝引以为傲的 do(>>=) notation 的情况不同! 🤔💭
duangsuse::Echo
#math 如何用一个【没有发生】的东西,证明一个发生了的镜子存在且以read开头?🤓✅ 反正 while "I AM": print(read()) 它可以从print开头,也可以从 not("I AM") 或者说 break; 开头,另一条世界线的宇宙“从一开始就是无,而不是有。” 因为苏格拉底选择用read(从笑和幸福)开头,我所理解的《数学》也要这么开头。广义相对论选择用print开头,是因为有人不明白 alias print=echo…… 可是人工智能实验室的Lisper知道的,哪怕祂“没写过”。🌚👌…
#FP 纯函数其实就一句话:“编程的人不调用「结果不一样的函数」”。
input(">>>") 和 random.pick(*"AB") 结果只在测试环境下一样,print() 在caller不能读取它(stdout流)时结果不一样,yield在耗时上结果不一样。
那么他们是怎么把代数搬到电脑里的呢?不是写vec2场函数,也不是挑战Rust宏或平民版comptime func,是把JS里的.then(f)做成“编译期查表出f”。
没错,去掉副作用只靠「不调用0参函数」(拒绝全局this和“立即树相等”),所以他们把任何函数都塞给了一个argLast:我们一般叫“调用栈”、“单步调试或执行”的那参数,还美其名曰高质量工程。数学和计算机就是扯淡,真数学的门(Torch/Taichi)FPer摸不到
用范畴论术语装点门面。范畴的集合可是有N:1箭头函数的,就连物理模拟也有2:1的sqrt(x)计算,哥德尔不喜欢听废话,什么时间和折叠(eval order)没有发生因为“代数生效了”。
Monad和 #Haskell 的Logo(>>=) 又是什么呢?结合律。
(read | greet) | echo 和 read | (greet | echo) 是等同的
(a;b);c == a;(b;c)
窝槽,这不是废话吗?不对。因为 greet(x) 有1个点位,所以FPer就造了整个“链式调用栈”,来发扬Curry和模式匹配GADT&class延迟绑定的精神,
其实 a;(b;c) 根本就是鬼扯,任何一个正常人都看得出Lambda不是这么用的
套那么多层无非是为了实现 if(Q,print,print) 和 if(Q,return,''), print 的“调用侧决定”,引用一大堆有的没的paper,
最后还不如 yield eventTuple 或纯粹的 DSL=(...lit)=>(env)=>{return value}
写操作系统和编译期函数的人早就知道了,DB/DS/CG 领域的人知道的更「纯函数」。
有这点能力不去写些有意义的DSL和“可以不乱的混乱语法”,去屠C++ template<>的龙,却凑一个死寂了70年的数学界的热闹。现在还想证明Dijkstra的软件测试知识是错的。
CS+Math,学术cosplay。 🎓 👎
我不两次踏入同一条河流,任何一个用户都是如此。 πάντα ῥεῖ
1958: Lisp → 解决实际问题(AI, symbolic)
1972: C → 解决实际问题(Unix, 系统)
1974: SQL → 解决实际问题(数据管理)
1983: C++ → 解决实际问题(性能+抽象)
1991: Python→ 解决实际问题(快速开发)
1995: Java → 解决实际问题(企业应用)
2009: Go → 解决实际问题(并发服务)
2015: Rust → 解决实际问题(安全+性能)
世界在变,数学没有意义,或者说数学的语言没有用。数学依靠语言,不是语言依靠数学。
如果代数逻辑都不够母语,还有什么是“整个宇宙的方言”?所以,没有一种有价值的领域,在担保上败给数学。
脱离涌现的几何知识是死的,死枝「挂名不上」活果实,就像量子物理承认了芝诺、费马、贝克莱大主教(牛顿的“好同事”)、高斯曲率定理。断言什么死的正确,真知识就复活来否定它。
就连算力的语言,也很可能是安排不够好而卡慢的。或许走一遍必然是 O(N),数学无穷里一定有一堆Node可以跑通 A->B,但谁告诉你N一定要有“那么多”?
"If 🍎 + 3 = 5, what is 🍎?" 🍎 = 1.
Accepted.
https://github.com/Kuri-su/yinwang.bak/blob/master/articles/函数式语言的宗教.md#:~:text=这个世界里的所有东西都是“有线”的。你需要绞尽脑汁
幸好我不需要线。没有吃掉苹果的人,才害怕那道线不对。 😊
GitHub
yinwang.bak/articles/函数式语言的宗教.md at master · Kuri-su/yinwang.bak
yinwang.org's auto backup. Contribute to Kuri-su/yinwang.bak development by creating an account on GitHub.
duangsuse::Echo
UNIXer觉得不支持()=>{}的C就是唯一的编程语言,而且libc的fork()隔离成本“极低”。
sched.c与vm.c,很大程度上是在【用最愚蠢的方法】实现fnarg2obj(闭包)和GC
sched.c与vm.c,很大程度上是在【用最愚蠢的方法】实现fnarg2obj(闭包)和GC
当然了,我简化了 accept() vs accept(then) 的问题,前者传入了自身pid(或者说整个ret2stack),后者另外传入了一个“代码和数据”双指针。 内核权限对后者是无能为力的,因为一个进程不能长两个头。
这个问题,真正是我说的“用非常愚蠢的方法实现闭包”:把 一个纯粹的token鉴权,和栈(pid)绑死在一起,所以有了各种语言的await。 这是养鸡场只有一个谷堆,一群鸡随机插着队来消化,但谷堆(网卡)本身是事件性的!😂
鸡确实没有两个头,但把进程视为单线的、有栈顶的,这种说法本身是错误的。并没有确定的执行顶部,只是一堆信号变量间的因果链!
😅因为OOP封装和“阅后即焚”式计算太流行了,好像必须要有个栈vs堆一样。其实栈分配只是个内联优化,泄露进了认知里,还成教科书了。 不会真以为栈变量就能阅后即焚吧?
这就好像某人发现了【文件名不占空间,可以拿来做压缩】,或觉得伪递归(continue fn(x-1)) 比for更占内存的“真理”一样让人哭笑不得。for还会死机,递归却允许爆栈时优雅容错怎么不说?
OS就是大一点的JVM,甚至代码解释器而已。 🐧因为sched/vm的内核功能把 “回调链表” 给藏在混乱的procfs统计里,就认为不存在持续态栈消耗这回事?
一个进程不能长两个头。 更深层次的问题是,pthread无法区分出这双指针里谁是“安全”的数据(需要喂给用户态全局sighandler)
😇我就摊开了讲:两个版本accept的本质区别是栈易失vs堆拷贝,是从
为了这个内核不能“跨域执行”的手短问题,你们发明了Kay的OOP里没有的毛病,这个模型并没有比“单线程”那个好看,因为仅仅是内核不能去直接eval那个回调而已。它必须区分arg和fun。
这件事的结果是,因为4K页和抢占式调度器太贵物的弊病,libuv 这些东西动态创建线程只为了在accept/read之类的固定行号卡死,然后push给master,后来又有了epoll/uring逐步的“跳步”支持。
本来的模型错了,但它的 dirty hack 碰巧掩盖了这部分。好像线程这东西真是存在的一样,好像“服务”真的都不是单线程的。
他们还经常说什么高并发L3缓存友好,其实就是一个不存在的问题,就像React的vdom vs Svelte的点对点绑定一样,错误的把“回调链表”用timer + while(1) if(ret!=null) 这样的若智逻辑表达,改回来,还长脸了? 你tm一开始就在无脑GOTO和鬼扯。
还有人又学到了 #FP Continuation,就是【回调链表】asks(A=>B=>C=>...) , #haskell IO Monad 给整的亲妈都不认识
让一切“返回”某种json再去diff减枝真是我听过最搞笑的优化了,JS觉得自己比C++快,C++认为x86中断不懂事件和因果链,编译器觉得机器和指针比人聪明。 根本没有“返回”,编程是点对点的。 🙄
这个问题,真正是我说的“用非常愚蠢的方法实现闭包”:把 一个纯粹的token鉴权,和栈(pid)绑死在一起,所以有了各种语言的await。 这是养鸡场只有一个谷堆,一群鸡随机插着队来消化,但谷堆(网卡)本身是事件性的!😂
鸡确实没有两个头,但把进程视为单线的、有栈顶的,这种说法本身是错误的。并没有确定的执行顶部,只是一堆信号变量间的因果链!
😅因为OOP封装和“阅后即焚”式计算太流行了,好像必须要有个栈vs堆一样。其实栈分配只是个内联优化,泄露进了认知里,还成教科书了。 不会真以为栈变量就能阅后即焚吧?
这就好像某人发现了【文件名不占空间,可以拿来做压缩】,或觉得伪递归(continue fn(x-1)) 比for更占内存的“真理”一样让人哭笑不得。for还会死机,递归却允许爆栈时优雅容错怎么不说?
OS就是大一点的JVM,甚至代码解释器而已。 🐧因为sched/vm的内核功能把 “回调链表” 给藏在混乱的procfs统计里,就认为不存在持续态栈消耗这回事?
一个进程不能长两个头。 更深层次的问题是,pthread无法区分出这双指针里谁是“安全”的数据(需要喂给用户态全局sighandler)
uring后来解决了。IOCP可能还简洁点,但如养鸡场一例所说,这是手写闭包和大switch的智障级设计。 为了一个“不能跨域请求污染”的事兴冲冲的把垃圾当成高性能做
😇我就摊开了讲:两个版本accept的本质区别是栈易失vs堆拷贝,是从
pix(x,y, implicit pid) 装箱转换为 enum Stack{Pix(x,y)} and pid {case Pix: .. } 的双指针的组合方法之别,大小写问题。为了这个内核不能“跨域执行”的手短问题,你们发明了Kay的OOP里没有的毛病,这个模型并没有比“单线程”那个好看,因为仅仅是内核不能去直接eval那个回调而已。它必须区分arg和fun。
这是模块化时的权限问题,其次是ld-linux的ABI问题。让accept自带fork的语意并不会让nice沙盒+mmap的开销更低,何况养鸡场并不适合为了while(1)设计的抢占式调度器。 把pid这个隐藏状态诚实的写成一个参数,怎么就这么难呢?
有多少人知道strace和stap可以像F12网络文件覆盖那样,模拟chroot? 好像pid和gui本身是不可再编程的最终产物。
这件事的结果是,因为4K页和抢占式调度器太贵物的弊病,libuv 这些东西动态创建线程只为了在accept/read之类的固定行号卡死,然后push给master,后来又有了epoll/uring逐步的“跳步”支持。
本来的模型错了,但它的 dirty hack 碰巧掩盖了这部分。好像线程这东西真是存在的一样,好像“服务”真的都不是单线程的。
他们还经常说什么高并发L3缓存友好,其实就是一个不存在的问题,就像React的vdom vs Svelte的点对点绑定一样,错误的把“回调链表”用timer + while(1) if(ret!=null) 这样的若智逻辑表达,改回来,还长脸了? 你tm一开始就在无脑GOTO和鬼扯。
还有人又学到了 #FP Continuation,就是【回调链表】asks(A=>B=>C=>...) , #haskell IO Monad 给整的亲妈都不认识
归根结底,异步编程就是个骗局。是事件的就归事件,不要把谷子堆起来,再责怪鸡怎么挤来挤去数据都乱了
让一切“返回”某种json再去diff减枝真是我听过最搞笑的优化了,JS觉得自己比C++快,C++认为x86中断不懂事件和因果链,编译器觉得机器和指针比人聪明。 根本没有“返回”,编程是点对点的。 🙄
duangsuse::Echo
#post #bin #os lib.so是库对象?是C模块? 谈谈纯函数、全局表容器、Worker线程模型 随着React的火热,大家应该都听说过「纯」函数式编程(#FP) 这个规范了。纯意味着不更改变量和外部状态(例如stdIO),但它和 .c 的ABI(.o 和最终输出的 .so) 有啥关系呢?😅 在点出关联之前,我要发表一段很典的评论: 90%的PFer不懂FP。 考虑一个 node &>/dev/null any.js 的环境,简单的 "log IO" 真的是“不纯的代码”吗? 请注意,stdout没法回环到原进程。…
#post #ce #haskell(IO) #os #js CPS是什么(异步和函数式编程 都可以看)
#ai锐评
🌶 https://chat.librechat.ai/share/hjL775AHhAji7HM8K0lFt
🧁 https://chat.librechat.ai/share/SmoUvMiuxY_hfmIcUEK95
CPS 不是 Clicks Per Second, 而是 Continuation-pass style,后者可用于优化前者。 😉
CPS的理由:CPS产生【回调链表】作为线程栈 📜,可以让单线应用像操作系统那样工作 ⚙️,但不能处理音画同步和多线下载(绿块块下载/暂停/断点续传等),这依然需要
CPS:把所有
CPS变换:如果
否则,需要处理
TCO:在递归时
Promise与Continuation:
将 ()=> 给 async 化变为对象后,它的 return 值就放在了
(这是一个GC引用问题,未完成而被await时,proc在sleep时,内存占用不会消失。c10k问题) 👀
(c10k和y2k一样是个傻逼问题 accept()明明依赖io_token,非要依赖pid,产生4K栈和CPU无效轮换的问题)
通过实现
有人 alias then值='酷酷的lambda' 或 'pthread线程',其实它更像 fn.bind(框架已知参数).bind(你的已知).call(运行时参数) 🤔。
这篇文章有点《图文不符》的意思了,其实还是有内容的,只不过AI味特别浓。
#ai锐评
🌶 https://chat.librechat.ai/share/hjL775AHhAji7HM8K0lFt
🧁 https://chat.librechat.ai/share/SmoUvMiuxY_hfmIcUEK95
故意生成的 emoji 🤗 AI味,看看逆不逆天?原文是手写的
CPS 不是 Clicks Per Second, 而是 Continuation-pass style,后者可用于优化前者。 😉
CPS的理由:CPS产生【回调链表】作为线程栈 📜,可以让单线应用像操作系统那样工作 ⚙️,但不能处理音画同步和多线下载(绿块块下载/暂停/断点续传等),这依然需要
Coroutine 调度器(事件循环/libuv)来补位。之后就和ChromeOS一样完整可用,脱离 C-like 世界的掌控了!🚀CPS:把所有
return x 换成函数语法如 onRet(x),因为只是调用,就可以玩两两组合了 🧩,比如 if(x=onCatch(x)) onRet(x) or exit(1) 或 addEventListener('click', ()=>onRet("两个按钮你点了确认"), {once})。没错的!✅ 你甚至能在 throw 错误的行号原位恢复,OR 手动实现 yield✨/callcc-setjmp/Algebraic Effects 依赖注入模式。CPS变换:如果
return x 都被换掉的 def 只是一个计算图(只包含赋值和 ret 变量),可以直接变成 switch(op++) case 0 case 1.. 的行号调试(每个 case 将不以 break; 而以 return; 结尾,c=a+awit b 变为 _1=wait;c=a+_1) 🔢否则,需要处理
for if 以及相关的流控,如 throw 和提前 return、爆栈的 DFS 等等,也就是扁平化为 goto(op=xx),f() 一次就走一句(几行的微任务)。 🚶♂️TCO:在递归时
f=(n, r)=>(n==0)? r : f(n-1,r+1) 是个特例,可以改写为 f(n,r)=return continue(n-1,r+1) or (n==0: r) ,这样的多分支状态机 🤖,所以叫伪递归(TCO)。另有一种将 f(n)=n0? 0 : f(n-1)+1 转为的【自动累加器化】,还有使用TCO(甚至模板解释器)优化 switch(opcode[i]){} VM的标准写法 ➡️ https://t.me/dsuse/21568Promise与Continuation:
(async()=>1)().then(one=> {}) 在一个CPS对象上写个 then 回调。🤝 因为每个 {} 块都有要“返回”到的行号,和此行号周围的【🎁已知变量元组/Upvalue/闭包】,这就称之为 then 值,粗略的认为它是F12面板你按F8暂停时 ➡️ 所指示的一行 【点开可看的函数名】。 将 ()=> 给 async 化变为对象后,它的 return 值就放在了
u._ret 和 rr=u.call(_yield的返回值=null)。rr 值用于优化 await 1 这样不挂起到 libuv 或 syscall 的缓存 ⚡️。 u._ret 即使在完成后调用 .then 也可读取哦!(这是一个GC引用问题,未完成而被await时,proc在sleep时,内存占用不会消失。c10k问题) 👀
(c10k和y2k一样是个傻逼问题 accept()明明依赖io_token,非要依赖pid,产生4K栈和CPU无效轮换的问题)
通过实现
onYield(i also{i+=1}) 与对应的 forEach,正如 onRet 与另一 Worker 上对应的 then,我们可以定义一个走1024次(4KB)才返回1次的 func* 📜,而不需要和 java 那样弄 iterator(){} 和 BufferedX(),这就是 Kotlin 最近的方向(但它们对响应式 flow 的理解不如 Svelte 🤷)。有人 alias then值='酷酷的lambda' 或 'pthread线程',其实它更像 fn.bind(框架已知参数).bind(你的已知).call(运行时参数) 🤔。
这篇文章有点《图文不符》的意思了,其实还是有内容的,只不过AI味特别浓。
Telegram
duangsuse::Echo
#ce py3.14 线性解释器(C实现)的新优化😝
- Tail Call Interpreter 是py3.14优化字节码解释器(大 switch(int) {case opcodes..} )的编译期优化,因为CPy跑def里的每条指令都有利用,PGO提速15% ,而且,perf现在能看单条opcode的开销了!
- [tailcail,兄弟调用] 就是跳转地址不在自身的 [tailrec,伪递归]。因为伪递归==改参数goto开头重来,而且,这种优化非常常见(相较于 [Computed Goto]),所以被…
- Tail Call Interpreter 是py3.14优化字节码解释器(大 switch(int) {case opcodes..} )的编译期优化,因为CPy跑def里的每条指令都有利用,PGO提速15% ,而且,perf现在能看单条opcode的开销了!
- [tailcail,兄弟调用] 就是跳转地址不在自身的 [tailrec,伪递归]。因为伪递归==改参数goto开头重来,而且,这种优化非常常见(相较于 [Computed Goto]),所以被…
duangsuse::Echo
我很好奇Scala现在的普及程度。它并没有像Clojure那样消亡
#Haskell 们长期连fmap就是现代H5里的derive()变量这点都没还原好。
直接把Promise弄成不人不鬼的 IO Monad,然后集合类不可变到处Lens(如同Rust的Rc RefCell套壳),结果还不如Svelte。都是回调链表,函数式的功能性和性能就拉胯一节, 不活该被淘汰么
安全${内插}和 o?.nulToNul 这些都属于是极其容易但价值高到夸张的, 所谓的 #> #| bash语法(自定义DSL)与之相比算不了什么。
究竟是天真到什么地步,会觉得mixfix生态位不尴尬? 可以bash或shadertoy里测试,为什么要用Scala写bash?能自动补齐? 需要检查pipe的类型吗?
Li Haoyi 的成功,和他所有库(os-lib, requests-scala 等)的流行,恰恰是因为他回归了Pythonic,利用了JVM上的生态位(kt太卡 Groovy太拉),但是native/js的scala支持咋样我就不说了。 我只能说, tstl (js2lua) 比较成功,这取决于SDK的阻力和output是否够原生。交叉到wasm上都不一定能有好果子吃
🤔 #Lua 其实是个非常奇怪的语言,它的迭代类似于range和JS整数键名的杂种,从1开始数,len和not运算也长的诡异,但只凭效仿SQLite就搞得许多lib(尤其是NVim/Wayland世界)不得不兼容它,虽然效果像H5兼容WASM那样是良善的
#js #FP #ai探讨
“这玩意儿就像 Excel 里的公式往下拉”
直接把Promise弄成不人不鬼的 IO Monad,然后集合类不可变到处Lens(如同Rust的Rc RefCell套壳),结果还不如Svelte。都是回调链表,函数式的功能性和性能就拉胯一节, 不活该被淘汰么
安全${内插}和 o?.nulToNul 这些都属于是极其容易但价值高到夸张的, 所谓的 #> #| bash语法(自定义DSL)与之相比算不了什么。
它很酷,能在特定场景下让代码看起来更“漂亮”或更“贴近领域语言”。 但是让我来选,我会毫不犹豫把${}和parserc作为一等公民,而operator重载和infix作为次要的。
究竟是天真到什么地步,会觉得mixfix生态位不尴尬? 可以bash或shadertoy里测试,为什么要用Scala写bash?能自动补齐? 需要检查pipe的类型吗?
Li Haoyi 的成功,和他所有库(os-lib, requests-scala 等)的流行,恰恰是因为他回归了Pythonic,利用了JVM上的生态位(kt太卡 Groovy太拉),但是native/js的scala支持咋样我就不说了。 我只能说, tstl (js2lua) 比较成功,这取决于SDK的阻力和output是否够原生。交叉到wasm上都不一定能有好果子吃
🤔 #Lua 其实是个非常奇怪的语言,它的迭代类似于range和JS整数键名的杂种,从1开始数,len和not运算也长的诡异,但只凭效仿SQLite就搞得许多lib(尤其是NVim/Wayland世界)不得不兼容它,虽然效果像H5兼容WASM那样是良善的
#js #FP #ai探讨