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
举例, #ts 上有”交并集类型“ (有人认为 hs, Rust 没有子类型所以不存在集合论的,确实如此但 in-out 形变是Racket子语言里也有的,我找不到原文但TRack 是有) (我一点也不羡慕没有重载和this模块等多态技术的圆括号 😅, 模型的缺失是拿宏手动兼容 a.b.c 所弥补不了的, 就像 F# 的 |> 比不上 xx.let{}, kt 的元组 destruct 比不上 ES6 let[x,y]=P, ES6的解构又不如 Prolog 的 unify [x,1]=[2,y]...…
#FP Prolog 和 Racket 一样都有严重的语法缺陷:没有真正的等号,语法一致性也很差;也就比 #SQL 好了
或许 Lisp 的嵌套链表很”统一“,但统一并不等于简单而一致

number(X) 就是 X=num() 的意思,很多算式本该有”一个值“,而不是为了”X=1 返回 [{X:1}]“ 就放弃简化
Res 变量应该被掩盖掉,就像Go等所有现代语言都抛弃了 struct*, char**

那样👆e(X) 就可以不用”宏“ 或 Monad 来实现隐式参数,化简3参数的 S=X+R, "2x"==2+'x' , 直接 num=[S,X,R] 就能被 repeat() 修饰了, num=["12a",X] 优于 phrase(num(X), "12","a")

(话说 hs 也真是垃圾🙉 连伪递归纯函数 都强制化了,却仍然不能求解出 x+[2,3]=[1,2,3] ?? 递归再回溯,不就是为了方便DFS吗?

确实 inc1(x)=2 可能需要闭包和重载,所以我说圆括号很垃圾,因为不够”广义“的等号,和那些扭来扭去的数学希腊字或者VB,PHP一样

🥰我最爱的方向是:
- 定义式/py,rb,元编程,跨平台接口
- 关系式/模式匹配,读写转化,typing知识
- 响应式/Vue ,await和协程, 编程时所见即所得
- 跨领域/基于物理,OS,libc 等根源问题的搜索习惯
duangsuse::Echo
#dev #algorithm #statement 「只要搜对了接口」 把问题写出来,就等于解决问题。顺便消灭所有事后、事前的坑 所谓编程语言能力,就是这样创建「伪接口」叙事的编程习惯,带来了治未病的价值观。 伪代码,不通用、不好思考 真代码,不易于溯源和穷举 我不需要「设计模式」,因为真实生活的对象之间的模式,比刻意设计术语和样板更加深广 我没有编过程,只是用数据的算式,展示算法和用法、答案和问题的本来面貌 OCR截屏, 字符画 <10行 fib 和杨辉三角、螺旋矩阵的定义 <20行 自动重构…
#py 周刊

#ai 方便中国用户离线安装的中文 Llama2-7B

Jupternb 7 支持 Jupyter Lab 的内嵌调试器
pip install manhole, pyrasite 支持向py进程注入代码

@pyscript.bridge def 可以提供js侧 py.func()
<script src="https://cdn.jsdelivr.net/npm/pyscript@4.0.6/dist/pyscript.min.js"></script>

awesome htmx 项目清单

pip install taipy
数据过滤节点图+mdx GUI

自动安装的免检测爬虫
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(ChromeDriverManager().install())

Py3.12 的 f"" 支持嵌套, 但仍不能被函数重载
#FP 最佳实践

pipx 已经支持的单文件依赖提案
#!/usr/bin/env python
# Requirements:
# requests

Flask 防范 IDOR 地址穷举攻击 (但不要学B站用AV BV号..

asyncio 注册自定义轮询任务检查任务回调的执行耗时


#jp 《每个人的线性代数》 [pdf 共10页]直接以可视化的示例概括了 ndarray([]) , 反观一些离岸国人一边以「哇你们线代好厉害哦」来认知一种数据工具, 一边吐槽中国的 CS 为啥那么烂 那么八股文
https://github.com/kenjihiranabe/The-Art-of-Linear-Algebra
《线性代数应该这样学》的英文版 Linear Algebra Done Right 最新版(第四版)开放获取了,可以免费下载电子版 https://link.springer.com/content/pdf/10.1007/978-3-031-41026-0.pdf
https://www.3blue1brown.com/topics/linear-algebra
教什么线代,直接看3b1b 用Numpy可视化计算
怎么不问问神奇的gpt呢?
如果只是停留在把自己当计算器的程度,gpt超过100个做题家
这个「一定的数学知识」随着时代和计算机的进步,一定是越来越少的
所谓生产工作,大家都能生产才有市场啊

把能解决问题的简单概念,用原创的废话转写,得到逻辑完美的新问题和“新”工具,便拿工具兜售自己对新问题的浮夸定义;这就是我对CS的观感。

就像一些 BASIC 时代的人,在吹“咱的语言都结构化流控了” “我又懂稀罕的古佬真理了” “逻辑式和响应式真的太特殊了” ,
殊不知时代仍在向定义式、生成代码、自定语法糖,在向语言去符号化 的方向走,快到都没机会让他们用留了一手的知识 漂亮地回击“恶意质疑”,便应“不体面”地求知若鸡 🥰实在太暖心

或许,这就是叙事逻辑的不同吧。 我没get到1行就显山露水的算法,有什么好高等的;我们大可用10页树立心智模型,来让核心只有1页,因为有规律 才被称为计算机「科学」。

技巧和功能有许多,在我眼里都是家常菜。作为一个合格的“大胃王”,私房菜这种东西,过眼云烟吧。

俺代码的接口越多,烧脑的概念便越少, 可能他们做的东西间没有联系,乃至结构和程序、专业和常识间争夺C位,那就完蛋了。

你用python写,你性能好,你下班早,你吃饭香,你睡的安稳
你用c写,它性能好,它cpu早下班,它内存吃饭香,它硬盘睡的稳
你说,应该死道友还是死贫道?
#tech #recommend #js
🧐从『函数』的定义就能知道,#编程范式 的发展史
👉 地址symbol、子程序void()、对象 SAM object、算式 x=>y 或文档树、 模式查询select [a b], a=1|a=2

x86无结构:是弱检查指针、面条流控、256项字符集,定长数组,栈分配,加载到内核mmap(rwx..不能为READ 段错误)
是dll内地址

C过程式:是向调用栈赋值后跳过去等返回,是子程序
C#OOP:捕获了局部变量(cellvar,可变)、原型虚表到this,是有Invoke(T)R 运算的对象

Py定义式:是依赖一些数据=>的算式
(HTML,CSS,RPC, py-ast) 是与调用参数对应的嵌套数据 (👍用调用解读内部项目,不关心返回)

TS关系式:是对x+y=2内变量容器 DFS出解集的嵌套(|,=)条件,先深解构到新变量,再构造

第五代:AIGC 不是形式语言,不便于列明框架和接口,但在描述常见需求时,能减少需协调的细节
NodeGraph(Scratch触控) 不是文本,不利于大工程紧凑度,但适合图表,AI

—语言特性是什么
向量:分量同算的2元组,或角度和力度;或按Nd(shape)推广四则的行列向量,👍ML CG DSP 大数据推荐 GraphQL4AI, MATLAB 之源
同构:把 "1" 的加法映射到Int上解决, 把"字节码"视为汇编而编辑,有 cat(X)Y, cut(Y)X 一组转换函数
微分:神经网络是有未知参数的算式(比如用 10=1,11=0 “点集”训练 XOR 函数), 使用"网络隐藏层"向前计算,靠损失(如MSE逼近原输出)做SGD,可以优化出"模型参数"

编译: 缓存流控和(结构)变量 等语法开销的解释期
解析:"abc 123" 吃前缀,吐出 常量,[],情况?, [1*2+3, 4]: [[ 'tuple' 1 2 * 3 + 4]] 树,方便多次深先遍历的 cat(同构)
分词:因为解析器不懂记录区间高亮、只重读最近一级'{}[]"" ',😇 想靠二次遍历 提供高亮、优化editor的性能
编码:C++ 可使用UTF16 L"你好" 迭代(解码)字符,输出时需压缩到UTF8。Windows 的坑多 exe可能是非Unicode.. TCP黏包:收发bytes时忘记填消息长度!

类型推导:让调用树构造出类型,可以含函数级未知变量: P<KV>就真的是编译期到处传的Pair存储。P<get T> 可为P<T1> ,Fn<set> 反之
模式匹配出调用(List,Fn)的成员交集,List<R> 就在匹配里统一,查找重载。


异常:可能为Err的返回值。需级联返回 伪递归:改参重来
事件驱动: 主循环if一些(用户输入,异步赋值) 变量的改变,触发相应的更新重绘函数。像CPU信号
Rx:函数间提供流(Channel)通信,来分配并发, 而不是 Promise.all 或Worker池的模式

调用链: T.f()={runJob; this as T}, 即 T.prototype["f"]=(*arg)=>(f.call(this,*arg) ,this.copy(K=V?) )
T自身的proto=基类, 直到Object不用再查"虚表". T限(ctor)函数, v8创查{}更快, 但可以用Map(需 get key()=equalsAsStr)

#FP 纯函数: 数据皆静止,调用皆惰性. if let 解构=值构造器可以含可变<T>
反射:即让(参数)类型 实现 Type 接口。在弱类型里元编程才算直观,可实现 toJSON, 0参创建,依赖注入(=按类型.ini) 等样板

协程:调用方自动传回调(即yield job 后的流.next)
因此,其“返回” 变为由(不卡调用栈的)子级们催促,终于(在某个调度器)计算
即函数级线程,回调,Promise 即 函续Continuation


需要注意的是, 向量矩阵是快70年前 Fortran 里就有的概念,协程30年前Lua就支持,Lisp 链表函数也是60年前的老体系

技术没有变好,只是用户的审美越来越刁了! 😇反观一些学院派, 竟然和”脚本小子“ 一样,每天把文档写成代码、 代码写成汇编 ……

程序员和卷王常以为,拥有知识,是天才的骄傲的事,是智商的”碾压“,行数越多人越值钱。 🧐但回看历史,一整代人都”会数学“,却只有numpy让物理公式,真正造福于生活。

人类走向现代文明,不是靠吃”聪明片“,而是”教学半“的合作与积累 ;获取信息是一项人权。没有开明的思想,技术不可能改进与繁荣。
能够触摸开源社区研究的繁荣,却害怕”核心技能“被人偷走,是无法融入正常社会价值观的体现
2
duangsuse::Echo
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 实际上,()=>…
#py #code #recommend 说到有趣的py接口,有个 iself()
def read_as_io(f):
class T(io.TextIOBase):
read=lambda o,size: f(o)
return T
-匿名对象
之前写贪吃蛇 snk.c 就用到链表
不想缓冲区溢出的话Vec还是好的。C里你要实现[] 需要懂链表/strcat, rust 里你写 Vec<i32> push 就好了
Rust的生命周期现在基本不需要考虑(尤其是对应用开发时), type infer 是非常自动的

# 匹配左右括号 paren
_c01 = {'(':')', '[':']', '{':'}'}
def eat(s,c1):
for c in s:
# 如果是左括号,就把它压入栈中
if (c1:=_c01[c]): ifErr='行号'; eat(s,c1)
elif c==c1:return # 如果是右括号,左括号便与之匹配
else: assert'','多余项而栈空' #仅支持纯括号文本, 否则要_c10,反向
assert c0=='eof','栈尚不为空'
-S表达式

optab={[';']:-1, "=;+ -;* /"}
exp=(s/*token o算符x单项*/, l/*evels 大则紧,深 *<+ */)=>{
let o,x=()=>add(Number(s())), ord=[],add=x=>ord.push(x),
at=O=>{let A,B; x()
for(o=s();(A=l[o])>=(B=l[O]);)if(A!=B)at(o);else{add(O);x(); O=o;o=s()} add(O)
}
at(";");return ord
}
-运算链

叠加整理了下思路 一个 T(x).f(arg): T 的链,并试图兼容还未f()的T
data Adder(list:Ln<Int>)
- add()=list(Sum).sum
- [].as( Sum(0, (A,B)=>A+B) )
["x"].as( Sum(it, (A,B)=>A+B) ) 就相当于 reduce,fold的 ,不需要class封装,用于支持分组过滤

在没有this的语言里调用链挺好玩的,但java里都有 new ArrayList(){{add(1,2); add(3)}} 这种用法
Player.MP3("")|:
time=length*.5
title=" hello ": "## ${trim()}"
start()


有趣的脚本语言:
Vlang (Go+Rust)
Esno (滤镜图)
Gravity(Kt)
class Vector{var..}
var v1 = Vector(1,2,3)
"\(v1+Vector(4,5,6))"
Wren(ES6+C#)
class Wren {
flyTo(city) {
System.print()
f=Fiber.new { //有点像Duktape
["small", "clean", "fast"].each {|word| Fiber.yield(word) }
}
while (!f.isDone) System.print(f.call())

coco Py(函数式)
def int_map(
f: int -> int,
xs: int[],
) -> int[] =
xs |> map$(f) |> list

Latte
class User(id: int, name: String)
object Singleton
interface Supplier
def supply
fun sum(a, b)
for i in 0 until a.size
println(a[i]+b)

val (x,y) = Bean(1,2)
o match
case People(name, age,Bean(x, _)) if age > 20 => ...



分词器
Lexer=kv=>{
let re=Object.keys(kv).map(x=>`(${x})`).join('|'), mf=Object.values(kv),m,i,j=0
re=RegExp(re, 'gy')
return (s,ontok)=>{
for(m of re[Symbol.matchAll](s)) ontok(
mf[(j=m.findIndex((x,i)=>null!=x &&i!=0))-1](m[j]), (i=m.index), i+m[0].length
)
}
}

f=Lexer({
'\\d+':parseInt,
'[a-zA-Z]+':s=>s,
'\\s+'(){}
})

rep=(txt,on, n=null)=>f(txt,x=> (x==null)?on(' '):
(x.substr)? (on(x.repeat(n)),n=null) :
(n=x)
)

buildAry=(f,a=[])=>(f(x=>a.push(x)), a)

rnSel=(e,A,B,o=new Range)=>(o.setStart(e,A),o.setEnd(e,B), o)
rnHL=(e)=>
e.oninput=()=>{CSS.highlights.clear(); f(e.textContent, (x,A,B)=>{
let on=tag=>CSS.highlights.set(tag, new Highlight(rnSel(e.firstChild,A,B)))
;(x==null)?0:
(x.substr)? on('str') :
on('num')
})
hout.textContent=buildAry(rep.bind(0, e.textContent)).join('')
}


document.write(`
<style>::highlight(num) {
background-color: #f06;
color: white;
}
::highlight(str) {color:red}
</style>
<div contentEditable id=hl></div>
<mark id=hout>
`)
rnHL(hl)
duangsuse::Echo
这是一个完整的Java式类型系统,支持泛型。 class List<T>{} 的语法对应Fun(T,ID())的数据。 Unit,Fn可视为不属于Any -'TR' Ln<T>.as(:Fn1<T R>)=Ln<R>(0) 也就是对 Arg((Fn),TR) (赋参数),深度遍历到 Ln.To,T.to(Int).. 就知道T具体是啥(还要支持map{map{}}..) 在T未知时结果是 List<R> ,但要生成代码时R一定被固定在Arg。这类把Var作为值的算法叫“合一 unification”。这是关系式(而非“顺序”)的,…
#FP #math 的一些 #statement
#tool html bookmarklet data:text/html;utf8,<body ContentEditable>

—10÷3=3…1
被大佬建议写成 =(3)3+1 即m=qn+r (r<q)形式的“短除法” ,理由是...没交换律
“理论家布尔巴基有个笑话: 1+2当然是2加1,因为整数对加法构成阿贝尔群”

当然,纠结这些除和除以工程意义不大
ax+i=b; x,i=divmod(b,a) 的可变量元组数学里可是没有。函数只是“关系”,没有sympy那种条理分明的元编程化简法,py那种无论匹配或访问都易懂的“类型化数据”

a%b 余数在进制计数法,KB MB单位,乃至红绿灯🚥 ,猜拳、[123]*3重复 里都可以用到,四则符号的推而广之是很方便的
但GL的vecM和距离函数, 对偶的cos atan2才算是真正的「函数图像」

对软件来说,值之间的关系、算式的归类、代换(元编程) ,都是值得思考的,这远比重复流行的技术栈重要。 形式化的语法,正是物理和编程比数学广泛的一点

— 纯函数的不可变吧…… 不是类型上'final' (btw. KT-override class 理论讨论)
意义真的不大, 无非是把堆里的变量放栈上来重赋值,或者栈变量换伪递归,拿能够DFS的写法却只做列表处理, 有什么用…… 又麻烦又慢 😓

变量关系式编程(LP)里, 函数式的 Memo f()=x+1 可以直接表达为 f(out y,x) 的细粒度重计算,解构时也一样! 不仅不存在“赋值”,也不需要那些纯函数的过度检查

一些人觉得 React是什么FRP,functional响应式 。但其实把可变数据,理解为含Var的不可变, 比纠结于List纯不纯好玩的多

类型姑且还是对心智模型的硬化, 纯度这些可都是虚无缥缈的风格格调了。有趣的软件未必靠FP来写。某些函数式lib的样板代码不比rs,go的少 ,代码质量只能是看人,是否憎恨冗余

—我想用 for()await varAssign; 替换组合力很低的 for await(x of {async*f(){ }}.f()){} 异步迭代器,也方便RPC/跨语言 的对接 #PLT
异步流每次next()只是Promise,但换成两个async()赋值--单监听, 用 var.Saw.to(x=>新值流)+AbortSignal 更一致

async()=> 的实现要调用 yields={*f(){ res=yield }}.f(), s.next(1) 里f的yield会把值和'自动回调'赋值到s.next,交给调用方调度。throw也可以直接next回调链表(CoroScope)里有catch的那层。
Iterator显然只是调度的一种特例,是把i++等流控 继闭包地从栈分享到堆。不能牵强地把等待恢复、迭代混为一谈

在ES5里,可以用一个 co(asy()) 来遍历 task.then(next) 并搭配 yield* tasks(下级)#Kt 协程则是直接把回调交给timer()等待,无遍历:

#js #code
wait=(n,ret)=>setTimeout(ret, n*1000)
we={*f(co, ret){
setTimeout(co.nt, 1000); yield //函续已经交出去了,现在该返回 就像f只是登次记
wait(1,co.nt); yield
alert('2s'); ret()
}}

_await=(f ,c={},s=f(c,()=>Job完成))=>(c.nt=x=>s.next(x), s.next())
_await(we.f)

把f变成大switch并不难,但Lua,Rb,Py依然在用运行期魔法实现yield:

we.f=(co)=>(co.nt=()=>{[ //先把f(yield)外提为x=
()=>setTimeout(co.nt, 1000),
()=>wait(1,co.nt),
()=>alert('2s')][co.i++]()
if(co.i==3)co.ret('like,co=Promise')
})()
we.f({ret(){'此谓调用约定,ABI'}, i:0/*func*则会等待1次next*/})

把co暴露给调用方更像JSPy (缺点是要由main调度then(next)),而把co暴露给下级 更Kt,Lisp (虽然更像回调, 但太隐式, 比如yield(x)需向Iter对象赋值 脚本没这作用域)
到了OS这级,键鼠事件,异步只是赋值+触发回调

—翻译一下, suspend fun f() =f1()+1

首先f()要是一个闭包(即 匿名class .new ),kt,Java ()->会自动实现这一步
然后,f1接受第二个this: (Int)->Unit 实现其return 。这就是JS的Promise.then 。你说的local vars 就是指 Promise or Continuation函续 or Async状态机(大switch) or Future..

f的执行也不能卡线程栈了,它也要靠回调,比如runBlocking等待其他Thread的信号量,或者在IO,Main等 Queue(函序) 调度

这么说,call queue 和单CPU多线程本质上都是分时复用,只是设备中断/SIGTRAP 被改为更高层的函数回调,CPU cycle 换成主循环(监听轮询poll)。 因此王垠觉得Lisp比C更适合写OS

Go,JVM,Ruby,Lua 的 stackful 模式允许不标明await 且仍支持返回值,而基于CPS回调的协程则兼容性好
不能把await 调用理解为有返回值的多进程,相反,函数自身是消息队列内的Task(即线程,可sleep),才能去"非阻塞"await

语句就是种顺序表达式, val x=1; f(x,x) 本该也能写成 f(x:=1,x) ,往往只是外提为val,所以await()就像 f(a?.let{return} ?: b) 是可以的

还有coroutineScope, sequence, select 等复杂的结构化信息。 这样能实现大goto(call/cc) 的功能,比如直接resume给某个 caller 的 catch(IO Monad) ,或者 yield 到 gen .next() ,DFS防止栈溢出等

yield的一种用途就是遍历Rx流,但它们生成的流却非异步(尽管它能"push"无穷项)。 另外我觉得RxObserver=next 比async yield 更合理

—「值和引用」别扯到C上
栈就是“非常容易做GC”的堆, 只因为在堆上就叫ref不算值 是不太定义式的,这暗示了 N2(x,y) 这样的数据“不该可变”-即便它说了xy都是变数
别把JVM当成C++的框架。调用栈是内存的一部分,函数值和对象都是“栈转堆”,return在内联后只是赋值;goto,参数并不配特立独行

copy和ref 的区别,只应在于后者是Var(mutable v:T)。 含var的struct也能纯复制,List也可以存栈上(调用在栈顶时,可以追加 int a[num])
只是对集合/事件/闭包等应用层共享,就必须有GC,Rc 来修剪对象图

绝大部分val=struct 的实现依然是堆内共享,与内联函数相当。只有int这样机器mov的值不用传指针

—编译和优化
>AI没答到点上。 我觉得synchronized和Mutex有什么区别, 就是取消 object.wait()才隔离出的

>竟然是让 withIndex() 驱动 get(it).. kotlin-stdlib 里的一些实现是自己在 for each 外 var i = 0 计数的……
智障,就像标准库不写 val x get() 写 fun getX
自己破坏自己提供的特性

>list set stdlib 里的这些集合库的indices属性都有优化
我说的是一般的 for (a in list) 优惠成 indexed for loop 去掉迭代器这种

都能让你手动改写法优化了, 要编译器和有两种IR的虚拟机是干什么?

编译器已经是优化了,虚拟机还要JIT, 结果应用层还是不能满足……
是啊,我就是觉得这种现况很可笑。kotlinc 还特别自己弄了一个内部IR,不知道是用于翻译goto 和协程还是啥

编译优化全都是不可见的, 你也不能知道llvm,ktc 执行完优化是啥样子,非得反编译
当然,clang,llvm 是可以导出IR的,但许多优化用的表示IR层级太低,正常人看不懂

这种情况的解释,就是IR的语意不能用语法来保存了, 但其实 loop/重复算式的外提,并不会破坏程序结构,甚至DCE都是要由IDE重做一遍

玩编译器那帮人根本不懂如何做交互式编程、图形化框架。 这一点 dnSpy 一个反编译器都比90%的编译器做得好
更别说同时有TUI,Qt,Web 三种界面,支持无数种bin格式的 radare2 了
duangsuse::Echo
#zhihu #PLT 又看到有趣的中文编程设计 ,做些科普。 👀 1.打这些太极 还不如就叫真假,另外,加语法糖并不是以混淆基本语法,以编程的一致性为代价. PHP,Ruby,Py2 和VB已经玩烂过这套“大小混写”了(你怎么不用神奇GPT编译呢?) 2.子类型对应的是强制兼容,而非隐式数值转换。 进制只是表示法,SQL那种类型只是验证位数 你说的不需要看(隐式新建类型变量)、运行时可见,Haskell、Kotlin也支持,但你对连 List<T>,T->R 都还没懂的人弱化类型,就利于他们学用新接口了?别把隐类型当成无类型…
#FP #js #kt 今天来桶白象 🤍🐘
>https://www.zhihu.com/question/624728280
"匿名递归,我们调用的是另一个函数。没有什么问题"
发现一个逆天的回答, 直接把调用栈函数理解为匿名+纯函数了,想递归还得加惰性Proxy..
>https://zhuanlan.zhihu.com/p/659387315
看到个同样逆天的

这个Proxy也能做,
argf=(rec,f=rec((...a)=>f(...a)))=>Object.assign(f, {of(F){f=F;return this}})
Y=argf
Y(f=>x=> x?f(x-1)+1 : 0)(10) // 多测试这条

argf(f=>x=> f(x)).of(console.log)(10) //而且还可再定,避免把arg0=self 到处传。

递归的好习惯是只思考1层-勿判断子项,Y都没做到这一点
Y=\rec. (\x.rec xx)(\x.rec xx)
Y=rec (\x.rec xx) (\x.rec xx) //注意, \x. 只是单个参函数
Y=rec Y
//不动点, noOp,id

\x.xx 在调用后把自己命名为x 交给自己(它就是对称形状),因此使用f就等于 f(f) (x)
看着很好但依然要把 rec(rec, x-1)+1 里的rec放堆上,但Y的意义就是「在不允许堆变量时,用传参实现可变」啊?

返回自己是伪递归,是循环,但 f(f)(x) 最后不是调用f,而是又生成一个f,只因你不能靠赋值,补充如何跳回自己这里(就与编译原理不一致了)

Y只是要证明lambda演算(S-Expr 的纸笔计算)可以递归……
Y = f => (x => x(x)) (x => f(y => x(x)(y))) //y=Proxy
为什么流行实现,和理论不同? 试试直译:
Y=c=>(f=>c(f(f)))(f=>c(f(f)))
//其中 (f=>f(f))(f=>f(f)) ()()()..这种"fork炸弹"会栈溢
请把 f(f)惰性求值
Y=c=>(f=>c(x=>f(f)(x)))(f=>c(x=>f(f)(x)))


不过 ycombinator.com 确实是因为这个「组合子理论」(某种Brainfuck🥰, 异于定义式编程里的"可组合")起的名
众所周知,爱讲众所周知都是别名大师, 他们的理论与既有概念间是隔离的,因此你要学习的内容就如 node_modules 那样多。 聪明的做法就是,先搞自己的,再兼容别人的
duangsuse::Echo
还有一些FP的错误 #design »=: (我们知道,OOP对象 constructor() 就是一个“多方法函数”,等同 when(arg0, {run:(self)=>overridedFun ,.})的闭包。是这样的「高阶函数」 - 函数作为值并不稀奇, 相反,滥用 partial(f) 与compose而非this链来隐藏参数,虽等价于 obj.bind.f ,却是一种无序亦无类型的表达习惯。typeclass是和C一样的无心智模型,这种散漫 不值得被崇拜 Python,Rust,Go 都…
#PLT #FP https://www.zhihu.com/question/28292740/answer/3297148810
看 moonbit 提到 (Node)Visitor 设计模式,觉得有趣就复述下 👀

Lit(1):Add(me,Lit(2)).eval==3

?? Expr
Lit'n'
Add'AB'(_:Expr)

- show me .:
Lit: "{n}"
Add: "{A.show}+{B.show}"

- eval me .:
Lit: n.[Num too]
Add: A.eval+B.eval

因为OOP无法外部扩展fun,但能加子类,所以请Visitor松耦合(py astor 靠字典,无需样板代码)

?? 'R'Expr.Vis
- as(:Add) R
- as(:Lit) R //因此 Lit'n' 有 - as(f:Expr.Vis) f(me)

me Show() [Str Expr.Vis]
- as(:Lit) "{n}"
me Eval() [Num Expr.Vis]

按道理 Show(add_3) 比 add_3(Show()) 优雅。但谁叫OOP是靠this @singledispatch 。不过OO的overload+override比Haskell归类性好多了

Oleg Kiseylov教授等人提出的Tagless Final技术,那就是直接抽象Expr的构造器集,这样函数会随着新子类被覆写,就像 Factory (天哪,FPer在搞事情前都不查重的吗)

-'V'(V [Int ExprV]) code1  V.Lit(1)

funcs Vshow [Show ExprV]
- Lit(NAME, :Show) show.res
funcs Vpostfix //逆波兰化

?? 'u'ExprV
^name
- Lit(:u)u
- Add'AB'(_:u)u

?? Column
Show(res:Str)
Eval(res:Num)

?? 'u'ExprV1 ExprV
- 添加的Node会被新V1show实现,因为现在Lit们 是能被class的函数

其实呢,[Show ExprV] 等于 [Str ExprV],这么写是因为 Haskell 的 class (Exprv t) 是按t重载的,不能手动指定Vshow

static成员实现不是subtype-=元类 时能做到的(这相当于有能+接口的“全局工厂” 比如说.. Gson 的那种)
同样, IntArray() fastutil 这些用复制粘贴特化泛型,也是因为 class 不是能被overload的 fun
但, #Rust 它们就是因为self参数,才这么无结构:

trait ExprGADT {
type Expr<T>;
fn lit(i: i32) -> Self::Expr<i32>;
fn neg<N>(A: Self::Expr<N>) -> Self::Expr<N>;
}

魔法并不是免费的,我们要牢记它的意图场景。
#FP #statement
https://www.zhihu.com/question/596624832/answer/3091106978

Lisp应该不算函数式编程语言吧,它唯一的好处就是在只有C--BCPL的世代支持了GC、选择了非符号的括树风格,用链表取代单步forif。 但是心智模型依然在C99附近徘徊啊,强检查数据之外,其范式并没有比C出众吧?甚至Lua的闭包和yield比圆括号们跨场景对称

范式说到底是「拆分数据上文」的方式啊。 无论FP的单方法函数、高阶组合、tvar窄化推导+GADT,还是OOP的多方法函数、this链、<T>+extends,直到运行时强转的语言……决定软件质量的并非自动测试,并非理论,而是对领域上文的正确列举

所谓PLT,编程语言理论, 就是为了正确理解应用惯用的输入结构、的分层次逻辑。只为尝试茴字的全N种写法 去学咋做翻译器,是种工程吧?

其实在我眼里,Haskell也不能算「定义式编程」的。 反而是numpy,ffmpeg,Matlab,Word这些软件拥有最定义式的、纯度最高的代码接口,而且,它们才是数学和线代的最前沿。把计科理解为数学的人,真的单纯得可爱。


一些连「行前缀重复」都消除不了的语言啊,连细粒度Reactive都改靠lazymemo的纯函数,鼓吹什么point-free (本质是this链) 和类型推导与扩展性,怎么看都不入「定义式+函数式」的大雅之堂吧?
无论类型还是函式、算式的定义语法上,都有2~3种“同样优秀方案”的语言, 能设计成这样的“形式化”语言,被称为优美且自然吗?

何况,基于 trait{} impl{} 与局部(associative type) 式的多态(multi-typed),不能说比子类型和<>类型好, 只能说是毫无使用处,而且在故作高深vs拖泥带水间反复横跳吧

倒不如说,这些语法项的选型,就是因为无用处才能显得混乱。 一旦有了能改变旧套路的灵感,没有什么理论是不能赋予价值的,哪怕只是看个乐子,都比只把跨界复制当作学习融入的人们强


编程语言呢,不是指责顾客挑食或“愚蠢”的 世代都有的聪明人。它是把烂程序、烂写法削好吃的厨师

魔法与应用,是不能对立的。 好的白魔法,终将取代黑箱子里的坏魔法。 像我这样,却会觉得JSPy,rb更懂纯函数DSL, HTML+CSS或JSON就是语法树和宏,Prolog就是更好看的SQL或Excel

编程技能不应该特殊化。打败程序员的,往往不是同行,反而是跨界的新手
有时也觉得 #CS#IT 界挺魔幻的,60~70年前的矩阵和协程,Lisp式的DSL,到今天还有框架 😔

当然,numpy和 Go func, kotlin.coro, Rust derive宏 已经远不是Fortran,OpenResty那些东西可比拟的

但究其根本,我觉得今天CS+IT的佼佼者,不少lib依然在犯70年前它们先辈的错误。
对于协程这个特性,究竟是DOM,node那样不靠package就包揽一切,还是像Go那样作为噱头,Erlang那样基于Actor,Kt这样与时俱进 最为正统呢?

拿js模拟过……
 总之就当自动传f1(回调),f局部变量在回调中, 所以调用f()能选择何时何处执行
Kt的结构化并发确实很有意思,但要谈可用性,JS的手动撤销和自动Dispatch更实在

btw一句,我讲的比较详细,但我对CPS不感兴趣,
主要是为了把回调链表与callstack、OS线程 做类比。确实编译原理和OS是分不开

本群人均素质挺高啊,都知道coroutine是靠CPS
但我更喜欢不纯粹的理论

有时我觉得这些也没必要说的, 我反复思考有好几年了,但它们终究是三句话讲完的程度呢
因为JS最开始是 func*(){} yield Promise,没有async

async是因为JS不能直接把函续交给then,要先yield给执行器(对等协程vs上下级协程)

Ruby的call/cc, C的"setjmp" 就是对等协程,它们符合CPS的刻板印象但不好用…… 总之异步编程反人类
异步编程也可以很美好的,比如 Reactive 就遥遥领先于Java,PHP这一套。 Nextjs应该是新时代的服务端语言

我想,大家都被限制在自己的编程生态圈,不去思考这些差异背后的进步方向,可能才是编程之道吧……

可能直到AI广泛辅助编程,这些问题都不会有答案
但我清楚AI无法完成写法的形式化,只能从根源上,让用户指定开多大线程池完成多少种Task,这些术语的差异才会伴随着“优化选项”消失

C 那种argc+argvec 的list,也只是术语的差异吗? 恐怕除了样板代码的多少,这些语言作为“app设计器”,也有根本不同
——
在我看来,无论是工业界最流行的语言,还是协程的理论讨论, 都是没有取得统一模型的
换句话说,每个语言有它的优点
这不是一件好事。 一个没有取得共识的工具,会阻碍人的泛化思考,会把编程当成一件机械的编码任务

其实明白编程和数学里的泛用性,又如何呢? 有人为此挣到钱,有人选择删了公开博客, 但到最后随着时代向前,知识也没啥可稀奇的吧

即便如此,我选择把编程视为一种乐趣和创作。
我不需要言辞间的“朴素” “平凡不难”,对于创作者来说,灵感,才能成为汗水里掷地有声的内核。 🕊
duangsuse::Echo
#design #plt 看 Svelte runes 有感。谈一些个人梗 《献给sets变量集和cat-cut等价性的悼词》 一个基于JSON调用图的跨语言虚拟机,所支持的函数逆运算,似乎让 Eqs(cat=toStr,cut=parseInt).flip.oncat("10", *2) 取代序列化的荣誉结束了。以后只是 await "10".as(Eqs($=>nbase$(10)), B=>B*2) Eqs.pipe([catcut链]) 不只是输在前缀重复上。担当上新的IO范式,对于异步的「链式文件另存为」…
现在Eqs 也删了…… 好了,现在新术语都断代了 😓哭笑不得
和之前删掉 onclick "监听器" 的灵感不同, Eqv.js 是作为曾经框架名的抽象, 虽然晦涩..但像React那样代数 装逼不是? 它也被新支持的「反函数」给杀了..

如何给一个js字典添加「响应性」,之前用过 obj.ref.x, it.x, sets.x, Eqs.x 这4种方案
没有想过用Proxy,那玩意是做DSL的,重载'.'号而已

ref是最合群的,但既不简洁也不直白。 it缺少赋值语意,考虑到此API不常用,简洁是次要的
sets 融合了 watchEffect, 或者说 sets((A,B)=>A+B) (A,B) 是一个完美的嵌入,可惜不能写 sets.base64(inp) (看看effect和memo的各种坑和linter,幸好我没脑子一热就开工)

那么用 inout((A,B)=>), inout(0).as(x=>监听), obj.Out.x(0) 一致了不少,且对应了 roAge, rwStrm 这些前缀缩写

虽然真不知道限制参数可变性有啥用…… 「共赋值参数」么, 这个模型把之前的Eqs双转换打败了。
它优美地解释了反函数被赋值事件执行,这是cat-cut 简写所没有组合力:即便要写from,into两函数,实质上也只有1个方向 .Out

Promise真的是业界标杆的渐进式抽象啊…… 那些什么Future,Task,Rx 都烂爆了。 希望inout也是这样准确的名字

btw. Vue主最近批判的 React doc 易错示例, 听说Rea家觉得宿主语言不够FRP... 但 miniKanren.org 可是能把JS做成Prolog, LP的样子呢

这我就要谈一点暴论了: Lisp 对函数式也是一个皮毛。看它繁琐的 let-in 和对 '+' 的内化就知道,是连写调用图、加函数重载 都很困难的树状VB链表控 ,拼简单它还拼不过py
至于 Immutable和Memo ,在 #FP 里是正义,但扯上范畴论就是牵强附会了,甚至连 "1".as(Out($=>nbase$(16)), A=>A+"0")==16 这种反箭头都描述不了,因为一切只在表面上禁止'=',并没有变量-变量的逻辑联系,甚至 变量-计算的模块化都是渣, 能把 curry compose的调用链当"特性",这作用域策略有多废啊?

看到那些会对我的工作造成困扰的"Feature" 和理论,我可不会随他们复述, 只需要对比个同类项 优劣自在人心

附:Vue为啥没$: 看完这个我对框架PL知识的匮乏有认识了.. #statement
C 里 array 没size
numpy 里采样没 sampleRate
Svelte 里 $(0) 不能返回,就像C++的栈分配
React 有了hook和effect各种样板冗余,diff还是很慢
CSS 的弹性动画速率xy轴翻转,居然能起in-out 3个名字,过渡不支持JS属性
Haskell 到处用fmap这个CPS -相当于then(),又自带懒yield,错误处理和(.all)异步却是烂的

WebComponent 没有js对象-UI DOM 绑定
Linux乃至UNIX, 主流OS的线程不能等返回值,但read() 却能挂起等待
这些框架的“值类型” 为什么那么 valueless ,为了性能? 为什么这些设计错误被堂而皇之地沿袭.. 这也叫函数式吗
补一句,为什么我那么在乎readXX()
首先,当然是它余缀了,Reader模式、Visitor模式需要加固定前缀是哪群大师教的?? Qt,luaY_parse 都没有这种文明
至少对 enum Op{Add(Op,Op); N(i32)} 写 visitAdd 的人绝对是py ast看多了,overloads不会用。 Java比C最主要的优势就是Type2namespace,居然有人主动添加余缀?

然后,是对编程界毫无进步的无奈。
我最初学编程时(8,9年前吧)改了一些c#小游戏,当时有 https://github.com/K0lb3/UnityPy?tab=readme-ov-file#mesh 的前身和一个闭源软件能解包素材,但只有后者能实现替换材质包的目的……

我就搞不明白,是因为特殊的加密校验方法所以不能重新打包,还是单纯开源版的人不会做。
(btw. 新版当然也是手写,没用construct那样的定义式库啦。 https://github.com/K0lb3/UnityPy/blob/main/UnityPy/files/ObjectReader.py )
难道这种需求很小众吗? 做一个reader的同时支持write应该是举手之劳吧。

但,呵呵,并不是啊! read():T write(:T) 才是新语言里通行的,它们不能向自己的参数写入,甚至向callback也不太会,也就是没法优雅地inout
往大点说,通过 fit(cellvarT) 允许多次dump()很好,但要判定isRd就显得不优雅了,不能体现“IO的方向”,不能“静态检查”-虽然实际上手写2次rw才是最易错、最无法compose的那个

就连我自己,也是从 class{fun r,w} 的组合器+React state 之类的东西开始使用no-return风格的。之前像mmap() 那样只靠设置arr[num],int typeTag; 来parse一个bin简直天方夜谭,虽然它在C/cpp里每天都在发生

简单的说,大部分C dev都知道哪几项构成了某个“数据结构”,而jspy人,并不懂,只是在玩栈转堆的抄袭,甚至连C那瑕不掩瑜的无长数组都没抄到手。
我不是在表扬C,但java取消指针时也遗漏了一些触及灵魂的东西,这令它的async API像个渣,也让Vue的诞生晚了十年。

今天的人用GC太多了,以至于离开new treedata 就无法编程
我讨厌导致这种现况的标准库/design pattern作者!
他们根本不知道用户需要什么功能。 他们只想要别人不明觉厉,这样才有人听他们说话出书

其实并非没有人用 declarative way 做格式化IO,给低内存的 xml-epull 加个参数 s.fitU8((x,onmutX)=>发事件或new并监听)
基于插入位置游标的链表修正,甚至能实现var-length结构的0copy编辑

把函数名变key、装进数组或kv,从而令「程序与所控制的数据结构直观对应」,我觉得这甚至不算FP的专利-它明明是“领域的口语”、“编程语言”、是“工程”师一词的核心价值

然而似乎FP bros也做不好这个,一大堆扭来扭去的符号功能不明,反而让无倾向的Declarative,为它们自以为高明的链表和括号站街,而动键盘的初心、致胜的精髓却被欺骗性极强标点符号埋没、被误解、被遗忘。
大概让人类编程,这个坏毛病是改不掉了吧。😊

--我不喜欢Lisp,但Friedman说的这句很对:好程序就该100%反映它所读写数据的结构。一切不执行的符号、重复的片段都是在凑字数,语言之罪,就让语言api去消灭;只有这样,作者才把精力放在正道上,大家才能用上不让人捉急的App

#statement #dev #fp
duangsuse::Echo
补一句,为什么我那么在乎readXX() 首先,当然是它余缀了,Reader模式、Visitor模式需要加固定前缀是哪群大师教的?? Qt,luaY_parse 都没有这种文明 至少对 enum Op{Add(Op,Op); N(i32)} 写 visitAdd 的人绝对是py ast看多了,overloads不会用。 Java比C最主要的优势就是Type2namespace,居然有人主动添加余缀? 然后,是对编程界毫无进步的无奈。 我最初学编程时(8,9年前吧)改了一些c#小游戏,当时有 https…
duangsuse: #读写线 #bin #FP #algorithm
1.所以我认为这种partial是错的,比如lexer吧,至少把Node的wsPre 保留下来-比如对注释文档,这样rust fmt/doc也会好写很多。

2.你举得例子太tricky了,而且JSON就不是一种minimal dynKV- 它有一大堆无效的;, 需要过滤

3.内存buf问题应该交给linux swap处理,而且用 fit(inoutT参数) 替代read():T 并不会阻止windowing

4.所以只要在pull的回调里注册onmut,就能免费定义出writeback(),当然实践起来确实没那么简单

duangsuse:
冷知识: int** 可以被scanf读取

C是存在静态数组的,i=0..sizeof a/sizeof int
https://blog.csdn.net/GGN_2015/article/details/119902369

我们可以确定一个共识: readXX() 的本质,是通过ret和赋值stack var把file的一部分加载到mem的KV乃至于[]里,这也是为何libc的tcp和inode都是 iostream API --以及为何会jspy的人不懂C移植

因此,虽然它叫IO,却被框在call-return并赋值、 forEach-call并write 的枷锁里,明明只是asn.1那样的databind手段,却被递归下降的样板代码给框住了

但这种模式在C里,就是struct,set_type和set_size的递归下降,可以靠os的vm功能缓存、1次读完 mmap():bytes,当然任何新语言都没有直观对应出这个

如果有些struct var 是不需要读的,完全可以用 isDbg: 来mock自己读到了,然后不实际赋值,反正JS是动态的

对了,你觉得为啥js版bytes(blob,abuf) 都不提供流API

难道它是想让用户手写py structs那样的封装?

py有cString,jvm有Reader,js没有byte流接口;那就是只想read一个二进制header喽?

TextDecoder那个啊,感觉java味大
尤其是async*() ,不得不用State(var)取代吧

https://developer.mozilla.org/en-US/docs/Web/API/Streams_API

最大的功能就是补丁fetch()和onrequest ,其他和WebSocket重叠了

duangsuse:
额。。就是讲用回调的人为何被coro不明觉厉到了,呗

Object的本质就是可扩展的fn.bind()
打包好避免难传递

用回调的人根本没有错,错就错在coro.then为什么那么间接,还没人解读

不同的OOP还是有区别的,虽然大家的实质都是在提供参数N->1和if typeof 这些CLOS都懂的函数式风格的技术

但Java对closureArg 样板化的支持(通过重写和重载)更好,这也是为什么MBPC classdef比lisp的defclass有意义
往大了说语法差异都是没有意义的,无非就是alloca和malloc、递归下降、流水线这些算法嘛

另外,这还有个魔怔的中英emoji双语嵌入式编程支持原型链 def T.class(vars):ret{}

https://gitee.com/chen-chaochen/lpk#https://gitee.com/link?target=https%3A%2F%2Flosu.tech%2Fplayground
橘橘橘子汁 & 🍊
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
#think #web #tool
https://cssgridgenerator.io/
https://crocotile3d.com/
https://fixupx.com/Oroshibu/status/1808500235263418441?t=BtGGBHj9jnN2AReHW2VTWA&s=19
三視圖?

有趣的建模工具
帅啊!
螺莉莉的黑板报:
(๑•‌ㅂ•‌)و✧
https://dataset.wav.pub/

https://brython.info/demo.html
https://github.com/Distributive-Network/PythonMonkey
#py
螺莉莉的黑板报:
https://github.com/baskerville/plato/blob/master/README.md

你们 rust 真是啥都能干……

https://www.sea-ql.org/SeaORM/

这东西好用么……求教……

https://gitlab.com/coolreader-ng/crengine-ng

有趣的引擎

https://chinese-font.netlify.app/ font
https://typst.app/universe/package/bob-draw
#svg

https://ivanceras.github.io/svgbob-editor/

#code
duangsuse:
大量复制粘贴十分抽象
list有那么难用吗
难道你没注意到近十行只变了1个参数
AIGC也没脚本化得这么严重吧

我看还是用list first!null好了,这么八股文…… 大家现在都是用get null取代预判has的

这种到处预判的风格,很像你找大佬问问题,先发「您在不在」,他只好回个在,然后你不在线……

表达式化比你想的要多哦:

>Spring如何把 @Autowired T xx; 换为 var xx=find(T.class)

var Ts = [T.class].map( applicationContext::getBean);

你在class{}里加那一堆定义,如果只引用了一次,就完全不应该引用。 到底都是要查bean的

从code smell来看,只使用了1次的变量名是无效的;如果只是为了易读新建变量,只能证明你的helper fn命名的有问题

kt已经有!! 了,还想要更多
Java 一大特色,注解属于元数据,和函数调用有重大区别

duangsuse:
那这是定义“随用随扔”变量名的理由吗
函数和变量是这么用的?
java以省略[""] 为由引入太多常量复杂度的优化了,都要所谓框架来解决

纯粹是make compiler happy,把一个schema弄成它能读懂的class,然后再强转Any来构造class而已

js里一个字典让java用就是这么麻烦,其实能用几次呢? init完就扔了,根本不是性能瓶颈。

注解的本意,应该和py的 @decorator 一样,是接受class元数据的编译期函数才对

而不是等到运行时才去查什么field有哪些@属性 然后逐个.set()

要动态生成一个类型,来获取注入,简直无理取闹。 明明就该在编译期产生Registry.get&fillNew的样板函数

这是属于既把元编程的路封死了,还不让基于注解的注入框架提供统一的“反射”方法

其实jawa懂这个道理,但它偏偏就做得很烂,就会个@Override
duangsuse:
为了编译速度这也太幽默了,其实,jvm作为栈机 基本就是带类型签名的GNU dc计算器,不需要什么编译优化

如果递归下降一遍,校验下类型 生成个push call 就能花什么编译时间,属于是用PHP的方式搞算法了

而且javac的能力还远没有到c预处理器的程度,为了优化? 我看是他们不在乎程序/类定义的优雅性

什么序列化啊,也没有做规范,和py能 import json,yaml,toml,marshal 能比?就一个用来RPC的ObjStream,还被业界遗忘,哈哈

汤姆猫是大专必学

虽然汤姆猫的Biz程度也就比Node http-server强半点吧

所以问题就是Result类型为何没有重载 !!, 让它看起来仿佛只是带有错误消息是null

凭什么!! 只能校验 Type? 呢?
assert也是乐观/悲观路径
Exception也是,为什么不能一起判定了?
还非得搞个ifEmpty, ifBlank.. ifNullOrBlank..
莫名其妙

duangsuse:
这不是废话吗?例如买30天大会员,肯定是30* 24*60*60秒后失效,这才是世俗意义上的电脑时间

看到py关于时间和日期/时差有3个API
我就觉得抽象

audit就是一个不懂AOP的,其实unix可以靠strace审查syscall,win32的 sysinternals.com 也有进程日志功能

与其自己搞个无人知晓的轮子,不如封装别人的,两全其美
把OS的活都包揽了,难道py是要运行在Ring0把内核驱动都带上? 那必须支持啊

绷不住了,抽象程度直逼jawa

一开始就不该搞所谓orNull
默认 first{}!! 就够了

我在js里都是把Error视为object返回值的特例,和null是一个意思
这样就不存在什么orNull orElse的,catch表达式也免了

就是语言设计者对值体系的理解不过关,例如NaN就是numbers的null才对,而不是什么number?
这样才能用统一的方法去报错/换默认值

可悲的是一开始就没把悲伤/快乐路径当问题啊,真是异常搞心态

duangsuse:
幽默IDE
完全相信它的API有一大堆根本用不上的class
忸怩作态的软件没必要去维护

kcp是这样的,可 import ast 自 https://python-future.org 就没变过😋
py只是用:隔离一个表达式先于=执行,就实现了高贵语言们的静态检查

既可以用于静态分析,也能在运行时查看f.annotation ,完备的数据不需要反射

btw. 大佬应该会做一个DSL
Message.invoke{ id==mid }
Message.select{ id==mid }//多项

可是这种写法才是真正的SQL逻辑式

既然Msg(id=)能构造数据, Msg{id==} 就能查询
这不是很合理么 #kt

很简单,如果做得太标准,以至于移植到任何一门语言里都没必要修改的话
就没人给官方docs贡献流量了

duangsuse:
https://www.liuwj.me/posts/ktorm-introduction/
🤔 一个SQL函数.xml codegen 能被吹爆,还有人卖课是我没想到的
ES6里一句 sql${} 解决的东西,哈哈

「没想到他文化程度那么低」……

怎么会呢,语句能缓存,再怎么也比一次加载完dao好

你要是不喜欢,或者不相信语法糖有缓存,可以做成生成函数的形式
例如
updateId=f=sqlFn2{a,b-> Pair{B/b, A/a}}
f("bye",0)

所以说,如果你不相信DSL能被缓存,可以转为生成sql函数的形式啊

这不是MyBatis++ ?

https://github.com/JetBrains/Exposed/blob/main/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Op.kt#L184

我只能说,缓存键要么是调用点或次数
要么是函数
似乎很难单独缓存DSL对象,除非对它们的构造器
幸运的是, key eq x 里x的修改能缓存,因为它是参数

https://jetbrains.github.io/Exposed/frequently-asked-questions.html#q-how-to-get-a-plain-sql-query-which-will-be-executed

https://stackoverflow.com/questions/74821893/postgresql-arrays-instead-of-extra-table-in-a-many-to-many-relationship
涨姿势了,以后 1:N用数组json N:N用join

duangsuse: #fp
要是kt能允许编译期生成一部分表达式,就没这问题了😅😅😅

可惜啊, unquote{bash(q(trim("trimedStr")))} 这当然是lisp的专利
其中q() 被生成为一个字面量,就像闭包捕获:
fun bash(s)=trim(s).let:
paste {system(it)}

并没有所谓的编译期,因为编译器能访问所有lib API


0开销不是瓶颈问题,但事关语言的脸面

明明就很有用,可被一些人理解为「并非瓶颈的东西」
然后被rustc拿来完魔法

要我说他们就是傻屌,只不过是把闭包「捕获」的实现方式,改为内联字面量(unquote)
就能轻松突破编译、运行的动静边界,根本没有引入任何新API ,没有什么循环展开和 kotlinpoet javapoet的区别

他们做成什么样了? 正则的缓存还不如py的拿dict查😅
duangsuse::Echo
>火之魔女: 不过unification我是想写好久了(应该按年计)但是一直没写的( https://github.com/duangsuse/mkey 写过,不过落后很久了 #sql 这算法就是 unify(a,b); unify(a,1); unify(1,a) 得出 a.v==1==b.v reify([a]) 解引用一下=[1] ,其实也挺有趣,包括npm也在牵强附会这玩意,真让人搞不懂从哪看的 http://minikanren.org/ 使用了一个自有的def: yield 实现,对 a&b…
#OOP #plt 学点设计模式
https://iota.huohuo.moe/OO-in-C.html#:~:text=一种设计模式对应一种语言特性%20%20By%20千里冰封

函数式人有许多「过度设计」,例如美 kotlin.Any.let 其名曰 #FP Functor.fmap (Arrow-KT.io)。这种(私货+算法)的泛滥,给IT带来了灾难,让许多初学者不明觉厉,也当然不能提升其应用能力(毕竟只是"指针别名"嘛)
https://kotlinlang.org/docs/scope-functions.html
https://www.ruanyifeng.com/blog/2017/02/fp-tutorial.html

但OOP也有自己的“私货”——用于弥补语法不足的“优雅的”设计模式
例如 Builder,Utils,Adapter,Delegate.. 它们是对this参数、对interface扩展不够灵活的变通

这个长文,我会用短 #kt demo 让大家看到 refactoring.guru 🐿收录的10种流行 Design Pattern 掩盖了哪些语言特性的缺失
以及科普"OVDEP" Observer Visitor(深先iterator) decorator(单参compose) EitherAB Proxy 等不逊色于箭头函数的,超越语言的优秀工具

## 创建性-Creational

这些模式是 new 构造器() 的变体,在Rust里被 fn new=Pair{A:1, B:2} 构造字面和 impl T for T1{} //mixin T1: T 混入取代

1. StructBuilder

有一个很长的SQL row需要new,可以使用默认值,插入前也有检查

class RwCol2<A,B> {
var A:A?; var B:B?
constructor(x:A,y:B){A=x;B=y}
constructor(){A=null;B=null} //默认值
}


我们把构造器中A=x;B=y; 拆成 newThis.A(x).B(y) 两个函数,就能实现初始值的解偶
填完数据可能要验证,如编译期验证不可变 fun build()=this as Col2

与它对立的是默认参数
RwCol2(0,0).apply {B=B+1}.B; buildList{} this参数上扩展 apply(T.()->*): T
K2 value class{init{}} 也实现了构造后验证,不过大部分人仍在使用专门的反序列化和data verify框架

2. (Abstract)Factory

常见误区是,Factory类似 Pair.of(1,2) 是为了重命名掉new,或者只把构造器集结起来: doc.createElement("div")
安卓 content.Context 与Button等View(ctx)的关系更像工厂的本意: 基于(操作系统)上文验证和保留数据

interface DataJVM {
fun <A,B>List(a:A,b:B): Pair<A,B>
fun <T>List(vararg x:T): List<T>
}
val kt=object: DataJVM {
override fun <A,B>List(a:A,b:B)=Pair(a,b)
override fun <T>List(vararg x:T)=mutableListOf(*x)
}
//val py=object:DataJVM{}


与它对立的是全局对象、元类trait。
全局fun弥补constructor钉死了 open fun 且难于校验的问题。当然!它也消灭了 object Singleton{} 和“双检锁”的样板
元类允许了 static (类名上)接口,而不是让可覆盖的函数代理一下构造器:
https://docs.oracle.com/javase/8/docs/api/javax/script/ScriptEngineFactory.html

3. Prototype

Linux对pwd等环境变量的fork()是最著名的原型模式,这使得父子进程间有了T1继承T 的关系
JS里 {} 等价于 {__proto__: Object.prototype} 函数表,函数表也可以随时添加继承。有 new.target 的构造函数()只是设置原型+赋值到this的简写
一切皆对象,除了 Object(1).valueOf() 里装了又拆箱的1。无原型的,只有更慢的 Obj.create(null)

data class 会自动实现 fun copy(),但它不能继承,因为无法拷贝父类的变量。Java里很难把对象加构成更大的子类(除了 inner class)
sealed class Box<T>(val A:T) {
inner class Pair(val B:T): Box<T>(A)
}
val x=Box(1).Pair(2)


按道理是x.A=1,那么Box(1)单例就是这些Pair的原型
#ruby #plt Quine 编译接龙 https://www.fxzhihu.com/question/30262900/answer/47877068
https://www.ioccc.org/2012/tromp/hint.html #fp lambda 元循环 解释器(https://www.zhihu.com/question/30262900/answer/49589781
Quine(蒯恩)一般是指能打印出自身源码的程序 _='_=%r;print (_%%_) ';print (_%_)

IOCCC,全称The International Obfuscated C Code Contest (国际难懂C代码大赛),顾名思义,是一场比谁能写出最“让人无语”的C代码的比赛。

btw. 同问题下有个 Shadertoy.com 类的C++答案,太丑了,把 vec3 拆成了RGB三个函数,代码重复率爆炸

还有两个中国特有的:
curl --connect-timeout 1 https://google.com 2>&1 >/dev/null &&你在中国
body.contains("的")? "UTF-8":"GBK"。 \u{E79A84} 在GBK是鐨殑,很罕见
duangsuse::Echo
看最近几条逆天,我要聊哲学 #CS 操作系统不就是4片3口虚拟化么 时间内存存储程序,网口线口板口 #os #plt #embed #recommend win,*nix,mac, aosp ios ;哪个不是只有这么点API和差异化 🦄?为了音视频和回应事件弄那么多框架外链新语法,不如3行 #web js。 像 bellard.org 那样的通才终究少数,搞出\0结尾无长度字串,连{}[]{type:}都没建模的libc算什么API啊?在位运算位flag上都被人打败 也配教人数据结构算法? 。 为这…
#FP #c https://github.com/hirrolot/datatype99/blob/master/datatype99.h macro
https://halfrost.com/reactivecocoa_macro/

tag union: enum { A(int) B(str) } in C 😨 无需任何编译器插件
implemented upon Boost/Preprocessor
冷知识: gcc 宏是可以通过CPS延长递归展开 到1024步的,而宏又包含 if defined/eq 等常规流控与##-concat等运算,意味着即便没有 template<> 魔法, for if 甚至 eval() 也可以 static_assert
// 9, 2, 5
static int lesser_than_10[] = {
ML99_LIST_EVAL_COMMA_SEP(
ML99_listFilter(ML99_appl(v(ML99_greater), v(10)), ML99_list(v(9, 2, 11, 13, 5)))),
};
// 用例: 1+(2+3)
datatype(
BinaryTree,
(Leaf, int),
(Node, BinaryTree *, int, BinaryTree *)
);
int sum(const BinaryTree *tree) {
match(*tree) {
of(Leaf, x) return *x;
of(Node, lhs, x, rhs) return sum(*lhs) + *x + sum(*rhs);
}
}


ML99 是一门在 C99 预处理器运行的lisp https://metalang99.readthedocs.io/en/latest/list.html
尽管如此,有大佬说「元编程」要三思 https://lotabout.me/2018/think-twice-before-utilizing-meta-programming/
btw. 看到文心一言禁止F12, 有人贴了个反制 很有意思😂
for (var i = setTimeout(";"); i-->0; clearTimeout(i)); 


利用宏展开和递归实现貌似不可能的链表处理,其实也只是手段巧妙,利用f.nArg这样的const拼接代码, 效果并不会比过程宏甚至简单的codegen好
#define AND(X, Y) AND_##X##_##Y
#define AND_0_0 0
#define AND_1_1 1

#define IF_ELSE(C, T, E) JOIN(IF_ELSE_, C)(T, E)
#define IF_ELSE_0(T, E) E
#define IF_ELSE_1(T, E) T

#define IF(C, T) IF_ELSE(C, T, )


我还是觉得,如果不限制工具链,编译期反射或运行期记忆化eval 比模板巧妙的多。 体操只是被编译器驯化的结果而已,比递归+模式匹配巧妙的算法有很多。

https://github.com/hirrolot/datatype99/tree/master/examples/derive
https://github.com/orangeduck/CPP_COMPLETE
https://netcan.github.io/2020/11/06/C-Rust元编程之BrainFuck编译器(constexpr-过程宏解法)/

n=a=>a.length
add=(A,B)=>A+B
curry=(f, N=n(f)-1, ARG=Array(N).fill('').map((x,i)=>'_'+i) )=>eval(`p0=>(${ARG})=> ${f.name}(p0,${ARG})`)

curry(add)
p0=>(_0)=> add(p0,_0)

完全有能力生成任意的vararg,并且可以加泛型
duangsuse::Echo
码聋魅力时刻|面向运气编程
#os #fp #statement
从脚本单步,组合到程序,本来就可以实现为调用堆叠、回调链表 两种形式,和浏览器的历史栈一样,都是记得结果赋值到哪,组合数据结构 来解耦程序的“菜系”,形成API,提高表达力。

如果说 inc (Box n)=(Box n+1) 就是纯函数,而 box -inc; 隐藏了致命细节,这就是因小失大。FP不过是把赋值写入了栈上,还模糊了改写时复制/useMemo的时机。修语法=修语意?

js流行的 Reactive 不过是把 let [A,B]=match_XML(..) 从栈上拷下来,支持自定解构脚本,不是什么发明。类似这样狭窄的理解能力在做infra/做原理的人里比比皆是。

为什么要区分调用和回调? 只是Stack内存和变量树的减枝,GC 绑定得太深,必须用 new SAM_POJO(..) 把 vars(let-locals+栈上临时cell) 甚至回调的行号(yield),外提为this,能跨越的范围才足够广,
比如能 JSON.load/dump,能放在IO轮询队列(甚至SQL)里,而不必元编程。 闭包和 Java 的 int vs Integer 装箱,完全一样。

例如把while(1)预处理成隔1毫秒yield一次的那种,和stdio行缓冲一个样,就要"pid"这个this了。毕竟……Linux-ELF就是个段错误版的.class,还不如WebAPI和esp32生态诚实。


因此,区分模块、功能点所必须的vars()树是绑定局部、参数、this、inline #def、模块KV/全局KV表? 都没有意义,前三者本来就是封装简化的核心,所以()=>和enum{Ok(), Err()}可能是对的,跨越了堆栈,没有boilerplates,但也可能是难以二次开发、层叠的元凶。

唯一的方法是从语意解构APIs,诚实的描述用户和开发者需要的模型,而不是生搬硬套拉丁文。

例如,能够热迁移一个pid,像老式游戏机一样存读档的 CRIU Docker 和真正的操作系统plan9,就很理所当然。 是硬件和二进制在拖后腿

那群家伙至今也不想承认没人真正在乎 i32 i64 的极值是多大,u32比i32好多少,对,就是搞出千年虫的那群nerd。为啥不叫 byte4 byte8 char4 好了? who cares?

或许这才是OOP和FP间选不出最优的最大原因:人群不同。
#py #FP 入演算 挺简单的介绍..
https://gist.github.com/vakila/3d5cebaebf01c4c77b289b9a0388e3c8

栏目答是「任何语言代码」背后的核心, 而编程的核心是自顶向下的组合代换,就像从[原子Atom]、生物学,到医学和人。 forif, 对应列表处理的 filterMap 都可以用"她🐑"实现!

data🐑𝛌 栏目答 = 算式Sexpr |
Sym | Abst [Sym] 栏目答 | Args 栏目答 [栏目答] — ({? (rand) exit quit} 1) 是有效的,不用 (..).call(,1)

data Sexpr = Ln [Sexpr] | Int | Str | ... # 非Line即Atom


有别于计算器的关键点是「Sym的地址」。它不能是全局表的"key",而是类似AST树洞、 x=await,可以放东西,无论是否“求值”、等多久靠谁“求值”、在编译期或调用堆叠上“求值”,只配合SDK/ABI即可实现App的运行。 Abst是Absent的缩写,求值=对单步化简(reduce)的深先替换。

栏目答喜欢宣传 Currying(颗粒化传参 A=>B=>A+B) 和 “用回调层数编码数字”,但那只是数学家的把戏。 也有人把第一层参数 (A) 称为this.A或nonlocal A ,那很有价值。官方翻译是 free vars, unbound 或 closure (module 私有)

栏目答一般用于代数(组合逻辑),它和无语句函数是等效的 (整个 callgraph /API 都不能有语句和{基本块}!因此,全局 vartree 也都没必要 let mut 了)。 JSON,XML loaddump, ...DSL 都可以用栏目答编写,有运行期/编译期template<> 的也可以! 😋

在 computation theory (类似WiFi vs 无线电理论) 上,栏目答并不太「图灵完全」 ,就像许多类型推理器,但它解决了仅为了沙箱化 while(1) 而多开线程的麻烦,越来越多工业语言在应用她。 死循环可以写 letrec 伪递归(调用 YCombinator polyfill 解决纯函数禁止全局赋值+取值自身的毛病)

f=栏目答 x: x+1
f=Abst(narg=1,nlet=0, [十(Arg$(1), Const(1)), ]) #, ...以(x+1)为参数继续调用

def Const(json)(stack): json
def Arg$(n=1)(stack): stack[-1][n]
def 十(A, B)(stack): A(stack)+B(stack) # jvm 直接把A,B默认bind到了求值栈上,这里写明,只是为了避免误解
十=(A,B)=> dumpOpcode('iadd', stack=>A(stack)+B(stack) ) # 协议的「语意」是用ID保证代码的跨端同一
@ https://tttttt.me/dsuse/21117
@ https://tttttt.me/dsuse/21179
@ https://tomstu.art/hello-declarative-world#functions-and-relations #sql 是关系式编程?

ps. 图灵完备和 Turing Award 无关,意思是可以把 QEMU.org 移植到某个编程语言里面去,但没有声音图形和点按设备。 这显然不是评价语法-语意表现力和融恰度的指标。

#tool #recommend
https://github.com/jupyter/jupyter/wiki/A-gallery-of-interesting-Jupyter-Notebooks 很好玩的 awesome-list !

#learn 纪念我在7年前的「编程入门」博客
https://gist.github.com/duangsuse/519411ab618ee57350ee2df93d33f58e
1
duangsuse::Echo
#cpp 抽象大佬 打起来打起来! 😅 C++ 最终会被manba out ,因为它的「性能」也是伪命题 编程从来只有「功能点×算法」, 性能都是藏在API后面随便换 本来就只应该有值类型 只要不mut,就没有引用了 我说的只差一点,只要整个变量树不mut,不“一键”equals,就不存在值和引用 而且引用的语意本来是和Vue ref() 一样的单更改多赋值,不是什么单内存地址的概念 为了几个块设备上的地址搞那么多有意义吗,有种自己写个不需要memcpu虚拟化的裸机应用 ref:https://tttttt.me/dsuses/5408
我感觉这群B友也还可以, 只是疑似有点极端了(虽然cpp届本身很抽象)
堆("json行"的内存/地址空间)、栈(调用堆叠) 不是什么x64ABI专属的, 是任何编程语言/#FP 都有的概念,只是被C++暴露相等性、begin<end(i<len) 这些1%的细节搞混了

https://drive.google.com/viewer?url=https://github.com/parallel101/course/raw/refs/heads/master/slides/memmodel/Presentation1MemModel.pptx
btw 翻译下段名:ELF对象的 text=fn rodata=lit data=rwlit bss=rw
他这个模型还不如说,是禁止指针运算和窄化宽化,从而只允许 &int, &const int 和CE修改器扫内存这样。 int[] 或元组对齐4 就很自相矛盾了,因为大端小端也属于实现细节,int可不是4byte之和

国内编程界经典拿“底层实现”当“本质”的遗毒之一,其他的例子还有谈虚函数(甚至谈子类型多态)必须扯虚表,谈Promise必须扯事件循环等等。必须用这种朴素的还原论才能理解上层的性质吗…其实恰恰相反,并非底层的实现决定了上层的性质,而是上层的性质要求了底层如此(只是一种可能的)实现

其实现在的内存分配器早就不基于堆数据结构了(叫自由存储区还比这靠谱点),函数调用时也不保证总会生成一个栈(例如内联成功的函数),局部变量有时也会被优化到寄存器上,全局常量也可能被优化到不占常量区空间。如果仍然假定总会生成一个栈,容易写出未定义行为,导致程序出错,不如学习更加正规的c++内存模型:函数体内的普通变量是自动生命周期,定义时构造,函数体结束时(抵达}时)释放;全局变量是静态生命周期,程序启动时(进入main函数前或dll加载时)构造,程序退出时(离开main函数后)释放;new和delete管理的对象是动态生命周期,new时构造,delete时释放。c++内存模型和堆栈内存模型并不只是名称上的不同,主要的区别有:每个独立的变量之间有任意大的不可访问区域,自动生命周期的对象所占内存并不和栈一样保证向下生长,new/delete,new【】/delete【】和malloc/free不得混用。更严重的问题是,堆栈模型只是说对象存储在哪里,并没有说明对象的生命周期(构造和析构的时机):例如要了解func(string().c_str());是否安全,但用c++内存模型就很容易解释:c++规定,临时变量string()的生命周期限定于func这一行,直到分号前都是有效的,而堆栈模型根本没有说明这样的情况,string究竟存在堆上还是栈上,你想了半天,想到了string有“小字符串优化”,小于15字节时会存在栈上,但这对于回答“临时变量什么时候失效”毫无帮助,只是增加你的心智负担,最终还是要回归正规c++内存模型来学习。有人说如果报错“栈溢出”不还是说明还是要学堆栈吗?函数调用栈当然是所有支持函数语言都有的特性,这里的栈要了解,并不能等同于那个连函数调用约定都一清二楚的底层堆栈模型,那就过头了,解决“栈溢出”并不需要了解x86函数调用约定,正如你在python中遇到“栈溢出”不需要查看python虚拟机里的字节码一样,不要偷换概念。类似的概念有“因为linux系统会报segfault所以必须学段内存管理”,然而现代操作系统早已废弃了段式内存管理,而是采用分页内存管理,SIGSEGV实际上应该叫page fault,segfault只是个已经约定俗成没法改的别名而已,正如所谓的“堆内存”早已不是“堆”数据结构一样。
“底层八股文”对C++编程的“积极指导作用”:比如,我知道GCC编译器的实现中,函数调用通常都是x86的call指令实现的,call指令会压栈返回地址,由被调用函数的ret指令取出。所以,我可以通过对第一参数的指针-1,构造一个指针修改返回地址,这样就可以读取或修改返回地址了!比如,我知道Python中GIL阻止了多线程并行,所以我通过ctypes.memmove强行修改了Python解释器内存中的GIL对象,这样不就可以自由的多线程并行了!我真是个天才,然而,我开启-O1优化后,发现GCC编译器把这个函数内联了,没有call指令了,我读到的返回地址有误,并不能形成完整的ebp链表,我认为这肯定是GCC编译器的Bug,我们老师明明说过函数调用约定会压栈的!我通过魔改GIL开始在Python中自由并行后,发现结果混乱,最终Python解释器在一声SIGSEGV中崩溃了,我给Python官方提交了Bug反馈,谁让他们不支持无锁并发数据结构的!另外,通过一个变量魔改另一个变量这个事有原型的,并不是我编的,请看:https://github.com/parallel101/opengltutor/issues/29 小彭老师的教学过程中,不论是群里还是b栈上,依然能遇到大量的“底层八股文”受害者,小彭老师看不得受害者,可你一和他“耐心讲解”,律师帽子就扣上来了。


哗众取宠。用于内存分配的堆栈这两个词在某个特定语言的标准里没有提到,不妨碍这两个概念在许多语言的规范或实现里都存在,说白了就是一个大家都明白的、有助于理解底层原理的抽象,本来就不和某个具体语言绑定。Up 在视频里列出的 strict aliasing 相关的 bug,规范角度说是因为标准里对怎么访问一个对象有明确规定,不符合规定的属于 UB;实现角度上说是因为编译器没有像程序员想当然的那样将变量连续分配,或者做了进一步的优化,跟堆栈这些概念有什么关系?告诉你有个栈,变量在上面就一定是紧密排列?更何况C++23 的 STL 里面还有 <stacktrace> ,如何能说“C++没有堆栈”?
看 up 的评论,貌似还想树个靶子说堆栈这个概念会跟同名数据结构混淆?那首先栈它在逻辑上通常就是一个数据结构意义上的栈,至于 heap 的词源已经很难考证了,未尝跟 binary heap 没有关系,但至多也只是名字起得不好罢了,既学过 binary heap 又学过内存的 heap 的人不至于自己查资料澄清的能力都没有吧。

>但很多时候你必须懂实现,懂系统和编译器特定的细节才能Debug[热]你可以说cpp规范里没这么定义过,但工作里碰到crash,分析coredump文件,运行时调试,必须要知道这些具体实现,甚至可以说不知道这些“实现”不能算一个好的c/cpp程序员

网上大量的经验分享和教程都不是面向纯粹语言的,而是面向问题、面向一个具体的bug的。
如果按照你这种说法,那么咱们说网络根本没有TCP/IP这种东西,只有osi七层模型,根本别学tcp协议和各种网络库了,这是不是很可笑?

无论语言律师如何追求纯粹的语言之美,实际写程序就是很难脱离平台特定的功能。出了bug,你也不知道这个bug来自C++语言层面,还是编译器或平台特定的问题,只能挨个排查。遇到编译器或平台本身就不支持或不正确支持某个C++标准规定的功能可太正常了…
请勿变成语言律师,堆栈结构是常见实现,只是我们不该把实现和抽象混为一谈。同样的bs也在自己书里把自由存储区和堆这两个名词混用。
https://www.bilibili.com/video/BV1Wr421c7MN
btw. PPT尾部的问题,简单来讲就是:C 中数组是第二类公民,数组是不能直接传递的,而 Fortran 可以值传递数组。C 中必须使用指针传递数组,效率非常低,因为编译器永远假设两个地址可能存在重叠(缓存失效),有了 Strict Aliasing,至少类型不同的情况能实现零拷贝