note
2025-10-12
尝试,翻了翻以前看过的东西,有关,编程的。
可能,大学之前都对编程没什么学习,然后,22 年的暑假,可能,才开始关注前后端的一些东西,为了对付大作业,用 java springboot 和 vue 糊了一个带评论区的 blog。
再之前似乎,没做到什么,让我有些实感的东西。虽然之前上课会学些数据结构 C, 数据库 sql,面向对象 java 之类的东西。但…他们可能还不如 css 更能引起我的兴趣。那时候,好像前端的组件库里每个组件都让我觉得新奇。
然后是在各种网站上瞎逛,偷偷看别人的 github 和博客 (虽然这种事我现在可能还在做) 。去腾讯云阿里云 github azure digtalOeacon 注册各种账号去白嫖学生额度…
那段时间我在上面放了许多乱七八糟的东西,从那个破破烂烂的 blog,到用了别人写好主题的 blog,几个我喜欢的应用,还有我后面写的小游戏… 但,基本还是自娱自乐,不过确实挺开心的。
可能,对于自娱自乐和做小玩具而言,前端还是更容易做到的,浏览器也是个足够丰富的系统。也是在这时,那些数据结构面向对象之类的东西,才有了些实感和熟悉感。
嗯…我是从看 vue 和 element 开始写页面的,然后才又像考古一样去了解那些更基础的东西。 html js css 之前也不是没见过,但,可能在一开始那些繁杂的细节让我眼花缭乱根本不知道从何看起,在 vue/react 这里看到了组件,状态,传参…这些概念之后,我才有余裕,回去更仔细的观察那些东西。甚至过了好一段时间我才意识到 vue/react 不是唯一的方法,那些组件库里的许多东西也早就被发明过,只不过提供了一种 vue 能方便使用的方法。一些优秀的 js 库也依旧以独立的 js 库的方式存在着… 一些更通用的好想法还会被浏览器和 es 纳入标准…
之后是找工打… 上班…
其实博客里之前有过许多编程相关的内容,但都删掉,藏起来了。
有些…笨拙,也没什么用。倒是余下的笑话片段多少还有些有趣。
也许那时所有东西都还很新奇,但现在看就是,像什么都不懂还爱絮絮叨叨的老妈妈,这种事情我自己知道就行了。
还是会,对一些写的很好的软件羡慕。 也许,他们天生能看清楚弄好更多事情…
2025-11-19
好想好好学习,可… 也不知道有什么好学的
会想,整理整理,看到的一些东西… 虽然感觉还是会写的很零碎…
Java
上班看了好多 java 代码…
- 调包侠 感觉,当调包侠,java 确实能用来做许多事。 单纯包的数量而言,也许 node 也不差, 但,一些重量级选手像 spring 全家桶… 数据库持久层,各种中间件适配…
以及,一些和所谓业务结合的更紧密的东西 ureport2, uflo activiti, urule drools 文档报表生成,“流程引擎”,“规则引擎” 还有另外一些的各种各样的类似低代码的操作。 ——虽然感觉这些玩意真的都是一坨坨的答辩…但也许这些又大又丑的东西就是能把好多公司哄开心了。
至于别的方向:
- 我还记得学校里开过的一些讲 hadoop hdfs hbase hive spark zookeeper 一类的东西的课… 虽然现在看来大半都是下下软件改改 xml 配置能跑起来就结束了… 其实没学到什么东西。
- 还有一条似乎是高并,各种发秒杀系统…
- redis, rabbitmq, 令牌桶. 削峰填谷。
- 拆分服务别让主服务也被秒了。
- 开始分析数据,开始假设,根据更具体的数据特点做优化。
2025-11-20
数据库
开始之前: 表,行,列,主键… E-R 图 基本 sql
之前的之前: 也许可以算是数据库的数学基础
- 关系模型 - 表,行,列,主键…
- 关系代数 - sql 语句各种关键字,的数学符号
- 关系演算 - sql 拼装与优化
三范式
- 1NF 原子性。关系中的每个属性都不可再分。
- 2NF 完全依赖。消除部分依赖,有主键,并且其他字段都只依赖于主键。
- 3NF 直接依赖。消除传递依赖。
三种范式可以看作在一步步消除冗余,到了第三范式,保证了唯一性,一个数据只在一个地方出现,保证可以方便准确的在修改时维护数据一致性。 对于业务系统,一定要遵守第三范式。 而对于数据统计分析之类的操作,没有精确处理的需求,做的宽松些方便开发与查询性能会更好。
事务与锁。
ACID.
A 事务原子性 D 事务持久性 是基础的基础,C 是需要达成的目标 而 I 隔离性完全隔离串行执行时性能太差,看情况允许并发:
- 脏读指一个事务没有完成就提交了修改然后事务失败回滚时,在提交后回滚前会读到错误的数据。
- 不可重复读指在一个修改前后读取数据,两次读取的不一样,读取的数据是正确的,但没有保证一致性,可能是曾经正确的数据。
- 幻读与不可重复读很相似,但进行的操作不是 update 而是 create/delete, 与不可重复读的区别是,不可重复读关注行内变化,而幻读关注数据量关注表的变化。
不管怎么样事务全程的 写锁/排他锁 还是要加的。
在此基础上,
在读取到读取结束加上 读锁/共享锁 等待写操作完成再读,可与避免脏读;
在读取到整个事务结束加上 读锁/共享锁 等待事务完成再读,可与避免不可重复读。
而解决幻读需要额外的机制,范围锁。间隙锁,防止在这个范围内插入新数据;Next-Key Lock(临键锁),行锁+间隙锁。
最后还有一个,可序列化,加表锁。
死锁:
死锁条件: 互斥条件, 占有且等待, 不可抢占, 循环等待 解决死锁: 死锁的防止
- 破坏死锁条件 死锁的避免
- 动态检查, 银行家算法模拟分配预演 死锁的检测与恢复
- 杀进程
再有就是,现代数据库并不完全依赖加锁保证隔离性,
MVCC,多版本并发控制,读操作访问快照不会被写阻塞。
引擎
B+ 树 索引
nosql
MongoDB, Redis, hadoop…
计算机网络
2022-12-31
给我的感觉是,这是一个贯穿硬件到软件还有协议的科目…链路层的某些东西像C2C,汉明码和计算机组成原理那边算是通用的,TCP 的各种算法调度又像是到了操作系统…
关于,五层模型。
物理层,提供最基本的连接,几种传输线和介质…传输线、信道的复用以及怎么一边复用一边避免冲突…
链路层就要考虑谁和谁连接和传输的有效性的问题了,这里出现了mac地址,交换机和c2c校验这种东西
网络层,mac地址依旧有问题,由于它的扁平化,分布随机,想要在较大网络相互找到很困难。于是有了IP协议,将网络地址的分配做成层级的,有规律的,相关的像子网划分、CIDR、DHCP、NAT…然而…怎么说呢,现在大部分网民似乎搞不到公网IP,ipv6,内网转发算是办法,但现在…从大的互联网公司申请服务,然后在它们ip之下的一些应用去获取身份似乎更加普遍。
在网络层解决了基本的相互找到的需求,而在相互找到之后的通信数据传输,则还要有运输层的TCP UDP之类的协议来保证,这里TCP的调度,滑动窗口,拥塞避免,重传,握手,等等等等…
再然后就是应用层了,http ftp smtp p2p来满足进一步的具体需求。这里也是之后会进一步了解学习然后使用的部分..毕竟搞软件,这是最直接最基本的接口…
操作系统
- 概述
- 进程与线程管理
- 进程与线程. 进程是“程序的一次执行”,线程是“进程中的进程”,是CPU调度的基本单位。
- 进程控制块, 进程状态, 进程间通信
- CPU 调度。
- 目标: 公平性、高吞吐量、低延迟、高CPU利用率.
- 算法 先来先服务,最短作业优先,时间片轮转. 多级反馈队列.
- 进程同步与死锁
- 同步问题,生产者-消费者问题,读者-写者问题。
- 死锁。
- 所以说数据库是不是也算是操作系统啊。
- 进程与线程. 进程是“程序的一次执行”,线程是“进程中的进程”,是CPU调度的基本单位。
- 内存管理
- 概念,逻辑地址 vs. 物理地址
- 连续内存分配 x
- 分页 - 非连续分配的典范
- 页号和页内偏移
- 虚拟内存. 将程序中暂时不用的部分留在磁盘上,只在需要时才调入内存。
- 请求调页, 缺页中断.
- 页面置换算法: OPT-理想算法. FIFO-先进先出, LRU-最近最久未使用。
- 文件系统
- 设备管理
在应用开发中的应用
- CPU 调度
- Kafka/RabbitMQ/RocketMQ等消息队列 多处理器调度。
- React Scheduler 时间片轮转调度和优先级抢占。
- Tomcat/Nginx 多级反馈队列。
- 数据库,资源分配与死锁。
- 内存管理
- xxx 管理系统也随处可见的基础的分页列表.
- Java/.NET的垃圾回收.
- 游戏渲染时远处变贴图,前端开发里的虚拟列表.
操作系统… 操作系统是什么呢。
关于资源的高效配置,任务调度。
可能不管做什么软件,做出来后,想要做的更好,这里的这些些案例会是很好的参考。
数据结构与算法
- 结构, 可理解的东西, 面向对象
- 算法, 效率与规模
这里也许可以看作某种…原料。
操作系统会使用各种各样的算法管理各种各样的结构。
而务虚的类型系统与编程语言理论会让数据结构可以推导演算,让算法有所依据,干净安全。
2025-11-23
计算机程序的构造和解释
其实这本书我根本就没翻过几次x
里面提到了三种抽象,
- 过程抽象
- 提取函数
- 嗯… 当然远不如此。这本书可能一上来说了一大堆递归迭代,高阶函数等等和函数式编程相关的东西…
- 数据抽象 和 模块化、对象和状态
- 从最简单的表驱动替换 if-else 到各种面向对象操作和设计模式也许都可以算作数据抽象
- 神奇闭包
- 元语言抽象-dsl
- 我其实完全搞不懂这地方到底要干啥😊
元语言抽象
元语言抽象的核心理念是:**我们不再仅仅使用一种语言来解决问题,而是为了某个特定的问题领域或编程范式,去构造一门新的语言。** 这种用来创造新语言的语言,就叫做**元语言**。SICP 用一个绝妙的比喻来解释这一点:在计算机科学里,我们仿佛站在一座由解释器构成的“塔”中。你写的程序被底层语言的解释器执行,而这个解释器本身可能是一个用更底层语言编写的程序,如此递归下去,直到硬件。元语言抽象让你有能力在这座塔中自己搭建新的层级。
第四章的旅程是循序渐进的,其核心成就就是实现一个可工作的 Scheme 解释器。
1. 元循环求值器 - 高潮部分
这是本章最核心、最著名的部分。你将用 Scheme 语言本身,来实现一个 Scheme 的解释器。这被称为“元循环求值器”,意思是“在自身之上循环的解释器”。
-
关键洞察: 一个语言的解释器并不神秘,它就是一个过程,这个过程能够:
- 求值: 根据语言的语法规则,对表达式进行计算。
- 在环境中操作: 维护一个环境,用于存储和查找变量的值。
-
求值器的核心:
eval和apply整个解释器就围绕着这两个相互递归的过程构建,它们是理解所有编程语言执行模型的钥匙。-
eval: 它负责求值。它的工作是根据表达式的类型来决定做什么。- 输入: 一个表达式 和 一个环境。
- 逻辑:
- 如果表达式是自求值表达式(如数字、字符串),直接返回它自己。
- 如果表达式是变量,就在环境中查找它的值。
- 如果表达式是特殊形式(如
define,if,lambda),就调用相应的特殊处理逻辑。 - 如果表达式是组合式(即函数调用,如
(f a b)),那么它需要:- 递归地
eval出运算符部分(即f)。 - 递归地
eval出所有运算对象部分(即a和b)。 - 将第一步得到的过程应用到第二步得到的参数上,也就是调用
apply。
- 递归地
-
apply: 它负责应用过程。- 输入: 一个过程 和 一个参数列表。
- 逻辑:
- 如果过程是基本过程(如内建的
+,*),就直接调用底层的实现。 - 如果过程是复合过程(即用户用
lambda定义的),那么:- 创建一个新环境,其外围环境是该过程定义时的环境(这就是词法作用域的实现!)。
- 在这个新环境中,将过程的形参与调用时的实参绑定起来。
- 在新环境中,按顺序
eval过程体中的表达式——这就是eval和apply的递归调用。
- 如果过程是基本过程(如内建的
-
-
“啊哈!”时刻: 当你完成这个元循环求值器并运行它时,你会恍然大悟:原来编程语言的魔法(变量、函数、作用域、条件判断)就是这么一些简单的规则组合而成的。你亲手用代码定义了你一直在使用的语言的语义。
2. 将语言作为设计框架
在实现了基础解释器后,SICP 展示了元语言抽象的威力:通过修改求值器,我们可以轻松地创造具有新特性的语言。
-
惰性求值:
- 问题: 默认的 Scheme 使用应用序求值(先求参数值,再应用函数)。但有时我们希望参数只在被用到时才求值,这就是惰性求值(正则序求值)。
- 解决方案: 你不需要修改你所有的程序代码,只需要修改元循环求值器。具体来说,修改
eval中对组合式的处理,以及apply中对复合过程的处理,让它们不是直接求值参数,而是将参数表达式“包装”成“延时对象”,只在需要时才强制求值。 - 启示: 你创造了一个新的编程语言,它拥有惰性求值的语义。所有在这个新解释器上运行的程序都自动获得了这一特性。
-
非确定性计算:
- 问题: 如何优雅地解决那些有多个选择、需要回溯的问题(比如逻辑谜题、搜索问题)?
- 解决方案: 再次修改求值器,引入一个新的特殊形式
amb(“ambiguous”的缩写)。amb可以在多个可能中选择一个,如果后续计算失败,解释器会自动回溯并尝试另一个选择。 - 启示: 你实际上实现了一个简单的逻辑编程语言(类似于 Prolog 的核心)。你为程序员隐藏了复杂的回溯和搜索逻辑,让他们可以在更高的抽象层次上描述问题。
为什么这一章如此重要?
- 祛魅: 它彻底揭开了编程语言和解释器的神秘面纱。之后你再看到任何语言特性,你都会本能地去思考:“这个特性在解释器层面是如何实现的?”
- 终极抽象: 它展示了最高级别的抽象——语言设计。当现有语言不适合解决问题时,最好的办法不是硬凑,而是为这个问题设计一门专属的语言。
- 连接理论与实践: 它将前面章节的所有概念——环境模型、词法作用域、一等过程、数据抽象——全部具象化地体现在一个实际运行的、你亲手构建的系统之中。
- 强大的实用性: 这种思想在现代软件开发中无处不在:
- 领域特定语言: 比如用于构建网页的 JSX、用于配置的 YAML、用于数据查询的 SQL。它们都是元语言抽象的产物。
- 解释器与编译器: 所有编程语言的工具链都建立在这些概念之上。
- 软件的可扩展性: 许多大型系统(如 Emacs, AutoCAD)都内置了脚本语言来解释用户代码,其核心就是一个求值器。
元语言抽象这一章,是 SICP 送给读者的一份终极礼物。它告诉你:
“你不是一个被语言规则束缚的程序员,你是一个可以制定规则的魔法师。”
通过构建自己的解释器,你不仅理解了计算机程序的“解释”过程,更掌握了“构造”新程序的终极武器——创造一门最合适的语言。这正是“计算机程序的构造和解释”这个书名的完美体现。 原来编程语言的魔法(变量、函数、作用域、条件判断)就是这么一些简单的规则组合而成的。你亲手用代码定义了你一直在使用的语言的语义。
// 创建一个简单的查询 DSL
class QueryBuilder {
constructor() {
this.query = {
select: [],
where: [],
limit: null
};
}
select(...fields) {
this.query.select = fields;
return this;
}
where(condition) {
this.query.where.push(condition);
return this;
}
limit(max) {
this.query.limit = max;
return this;
}
// "解释器" - 将 DSL 转换为实际的查询
build() {
let sql = 'SELECT ';
// SELECT 部分
sql += this.query.select.join(', ') || '*';
// WHERE 部分
if (this.query.where.length > 0) {
sql += ' WHERE ' + this.query.where.join(' AND ');
}
// LIMIT 部分
if (this.query.limit) {
sql += ` LIMIT ${this.query.limit}`;
}
return sql;
}
}
// 使用我们的 DSL
const query = new QueryBuilder()
.select('name', 'age')
.where('age > 18')
.where('status = "active"')
.limit(10)
.build();
console.log(query);
// 输出: SELECT name, age WHERE age > 18 AND status = "active" LIMIT 10
// 创建一个规则引擎 DSL
class RuleEngine {
constructor() {
this.rules = [];
this.facts = new Map();
}
// 定义规则的方法
rule(name, conditions, action) {
this.rules.push({ name, conditions, action });
}
// 添加事实
fact(key, value) {
this.facts.set(key, value);
}
// 规则引擎的"求值器"
run() {
for (const rule of this.rules) {
// 检查所有条件是否满足
const conditionsMet = rule.conditions.every(condition => {
const [key, operator, value] = condition;
const factValue = this.facts.get(key);
switch (operator) {
case '>': return factValue > value;
case '<': return factValue < value;
case '===': return factValue === value;
case 'includes': return factValue.includes(value);
default: return false;
}
});
if (conditionsMet) {
rule.action(this.facts);
}
}
}
}
// 使用规则引擎 DSL
const engine = new RuleEngine();
// 定义规则
engine.rule(
'discount-rule',
[['total', '>', 100], ['customerType', '===', 'premium']],
(facts) => {
console.log('应用 10% 折扣');
facts.set('discount', 0.1);
}
);
engine.rule(
'free-shipping-rule',
[['total', '>', 50]],
(facts) => {
console.log('提供免费送货');
facts.set('freeShipping', true);
}
);
// 设置事实并运行
engine.fact('total', 120);
engine.fact('customerType', 'premium');
engine.run();
还有在再前面提到的 规则引擎 ,
一个 dsl,要设计脚本语法,能编译到某个平台运行,能够描述/实现一些业内常规的范式/功能;然后还要跟着低代码的风要实现一套拖拉拽画逻辑的界面。
虽然听起来挺厉害…
然鹅…
它也可以非常… 嗯…
接地气…
JSON定义结构 -> 字符串模板(FTL) -> 拼接成脚本语言代码(Groovy) -> 动态执行。
这里没可以没有什么编译器,有的只是通过拼接字符串替换模板上的段落,
这也许可以算是是一种“穷人的编译器”。
绕开了所有编译技术中的复杂环节,用最朴素、最直接的方式达到了目的:将结构化的数据(JSON)转换成可执行的代码。
它也许支持不了复杂功能,调试报错信息可能也一塌糊涂难以调整… 但它确实能跑就是了…