读《编程之道》二分 - 庄子对程序员说
flyfish
庄子言论和编程之道
书中说:寂静的虚空里诞生了神秘的东西,这种东西恒久存在永不消失,它是所有程序的根源所在,我不知道怎么形容它,姑且称它为编程之道。
计算机是啥
最早的机械计算机就像个大齿轮箱,用齿轮转多少格来表示数字,压根没用二进制;为啥?因为它们都能按规则自动处理信息,这才是核心。
现代计算机用0和1其实是个“技术偷懒”的选择。电子元件比如晶体管,最容易稳定在“开”和“关”两种状态,高电平就是1,低电平就是0,这样做逻辑门电路(像与或非这些开关组合)特别方便,成本低还可靠。
19世纪巴贝奇的分析机用齿轮和杠杆算十进制数,靠打孔卡片输入“程序”,跟现代电脑用电子元件算二进制本质上是一回事——都是“机械/电子结构+规则输入=自动计算”。ENIAC最早其实是十进制的,后来发现电子管做二进制更高效才改的,如果存在三值逻辑计算机、DNA计算机,都是换了套“工具”,但“按程序自动处理信息”的内核没动。
从数学角度看,计算机就是把算法“落地”的机器。比如算圆周率的算法是一堆步骤,计算机把这些步骤转化成电子元件的开关动作,0和1是步骤在硬件里的“翻译”。
计算机是通过可编程规则自动处理信息的通用系统,其核心能力是“逻辑自动化”和“抽象信息处理”,这一本质不依赖于具体的物理载体,0和1是现代计算机的物理表示形式,是技术实现的产物;
如今AI技术蓬勃发展,AI让“可编程规则”的内涵和实现方式有了质的拓展。传统编程中,规则是“显式指令集”,程序员必须用代码把每一步操作都写得明明白白,这些规则是确定且可穷举的。比如计算圆面积,只需写下公式“面积 = π × 半径²”,机器就能按部就班执行。但这种方式面对复杂场景时就会碰壁,比如让机器识别猫和狗的图片,人类无法用代码穷尽所有动物的特征差异,也无法覆盖光照、角度等动态变化的干扰因素,传统编程的局限性在此暴露无遗。
AI编程则跳出了“人工硬编码”的框架,其规则由“学习算法 + 数据”构成:一方面,人类需要编写固定的学习算法(如神经网络的反向传播机制、梯度下降优化方法),这些算法就像给机器搭建了一个“学习框架”;另一方面,机器通过海量数据训练,自动生成模型参数(如神经网络中的权重矩阵),这些参数隐含了从数据中提炼的规律。以图像识别模型为例,人类编写框架代码后,机器通过分析百万张图片,自主“总结”出“猫的耳朵多为三角形、眼睛分布在面部特定区域”等识别规则,用“统计规律”替代了传统编程的“确定性规则”。这种方式解决了复杂问题,是通过数据和算法引导机器执行任务,学习算法是人类设定的“元规则”,模型参数是机器基于元规则从数据中推导的“子规则”。
从实现逻辑看,AI系统的底层依然是程序控制:所有AI模型都依赖底层代码实现,比如TensorFlow、PyTorch等框架提供了算法运行的基础设施,大模型的Transformer架构每一层计算都基于明确的数学公式,训练和推理过程严格遵循算法规则。区别在于,传统程序的规则是“硬编码”在代码中的(如if...else
条件判断),而AI程序的规则隐含在模型参数中,通过矩阵运算、向量变换等数学操作间接体现。这种“参数化”的规则表达,让AI具备了传统程序没有的灵活性——比如自动驾驶系统能根据实时路况动态调整决策,本质上是强化学习算法根据环境反馈不断优化策略,但其核心仍是算法规则的结果,而非机器自主产生的意识。
如果未来AI催生“元编程系统”,自动设计机器学习算法,看似让机器具备了“自编程”能力,但这本质上是人类设计的更高层规则在发挥作用——元编程系统本身仍是由人类编写的代码驱动,其生成的算法也必须符合既定的数学和逻辑框架。无论技术如何演进,计算机始终是“执行规则的系统”:传统时代,规则由人类手工编写;AI时代,规则由人类设计的算法自动生成或优化。规则的源头始终是人类设定的目标和框架,执行主体始终是遵循物理定律和逻辑规则的硬件与软件组合,计算机从未真正脱离“可编程”的本质。
书中说:道生机器语言,机器语言生汇编器。汇编器生编译器,最后产生上万种高级语言。
机器语言 → 汇编语言(符号化抽象) → 高级语言(脱离硬件抽象)
↳ 汇编器(一对一翻译) → 编译器(复杂语义转换) → 解释器(动态逐条执行)
↳ 现代混合执行(JIT 等优化技术)
语言进化:面向机器(二进制语言)→ 面向硬件符号(汇编语言)→ 面向问题逻辑(高级语言)
机器语言 → 汇编语言(符号化抽象) → 高级语言(脱离硬件抽象)
↳ 汇编器(一对一翻译) → 编译器(复杂语义转换) → 解释器(动态逐条执行)
最早的计算机编程完全是“面向机器”的状态,编程简直是“反人类”操作——那时候用的是机器语言,全是0和1组成的二进制指令 。打个比方,这就像你跟机器说话只能用摩斯密码,而且每台机器的“密码本”还不一样。比如要让CPU做个加法,你得写成类似“00110101”这样的二进制串,不同CPU对应不同的编码,程序员得像背字典一样记住几百个这样的二进制组合。更麻烦的是,写完程序得把这些0和1打孔到卡片上,要是有个孔打错了,整个程序就废了,得重新来过。那时候程序员就像在黑暗里摸黑干活,写个简单的加法都得反复核对,效率低到让人崩溃,这就是“面向机器”的时代——所有操作都直接对着硬件来,人得迁就机器的“脾气”。
后来到了40年代末,有人受不了了,想着能不能用符号代替这些二进制码,让编程稍微“人性化”一点,于是就有了汇编语言。这时候的编程算是“面向硬件符号”了,比如用“ADD”代表加法,“MOV”代表移动数据,“JMP”代表跳转,这些助记符就像给二进制指令起了“外号”。比如原来的加法二进制码“00110101”,现在写成“ADD A, B”,一看就知道是把A和B相加。但汇编语言写的代码机器还是不懂,得有个“翻译官”把这些符号翻译成机器能懂的二进制,这个翻译官就是汇编器。不过这时候的编程还是没脱离硬件的“束缚”,比如不同CPU的汇编语法不一样,你得知道寄存器怎么用、内存地址怎么算,写代码的时候还是得想着硬件结构。就像你学外语,虽然不用背字母组合了,但还是得按人家的语法来,本质上还是在跟硬件“对话”,只是从“摩斯密码”变成了“单词”,这就是“面向硬件符号”的阶段——用符号简化了二进制,但核心还是围绕硬件操作。
到了50年代中期,程序员们彻底忍不了了:凭什么我写个程序还得懂机器怎么运作?能不能弄一种语言,让我直接用数学公式和自然语言的逻辑来编程?于是“面向问题逻辑”的高级语言就诞生了。可以直接写“C = A + B”,不用管A和B存在哪个寄存器里,怎么取出来相加,编译器会帮你把这行代码翻译成对应的机器指令。这就像从“外语对话”变成了“说母语”,你只需要关心问题本身——比如怎么算圆的面积、怎么排序数据,而不用管机器底层怎么操作。高级语言把硬件细节全封装起来了,你写代码的时候就像在纸上列数学公式,或者用简单的英文句子表达逻辑。比如Python里写“print(‘你好’)”就能输出文字,完全不用想这背后CPU怎么控制显示器。这种“面向问题逻辑”的语言让编程真正变成了“解决问题”,而不是“跟硬件打交道”,就像你用计算器算乘法,不用关心里面齿轮怎么转,按按钮就行——高级语言就是让程序员专注于问题本身的逻辑,把底层的硬件操作交给编译器或解释器去处理,这才是编程真正走向“人性化”的关键一步。
不懂编程之道的程序员常常把空间和时间消耗殆尽,得道的程序员则总是有足够的空间和时间去完成编程任务。
先搞明白:啥是时间复杂度和空间复杂度?
(1)时间复杂度:算法跑多快
举个例子:比如你要在一堆书里找一本叫《算法导论》的书。
- 最笨的办法:一本一本翻(类似“线性查找”),书越多,翻的次数越多。如果有n本书,最坏情况要翻n次,这时候时间复杂度就是O(n)(O是“大O符号”,代表一种增长趋势)。
- 聪明点的办法:先把书按书名排序,再用二分法找(类似“二分查找”),每次把书分成两半,只查一半。n本书最多查log₂n次,时间复杂度就是O(logn)。
时间复杂度不是算具体跑了多少秒,而是看数据量n变大时,算法的运行次数以什么“速度”增长。比如O(n)是“线性增长”,n翻倍,次数也翻倍;O(n²)是“平方增长”,n翻倍,次数变成4倍,这就叫“复杂度高”,算法慢。
(2)空间复杂度:算法占内存多少
再举例子:你要存100个数字,可以用数组存,每个数字占4字节,总共400字节,空间复杂度O(n);但如果这些数字都是1到10之间的数,你可以用“位运算”压缩存储,比如一个字节存8个数字,100个数字只需要13字节左右,空间复杂度还是O(n),但实际占用少很多。
核心逻辑:空间复杂度算的是算法运行时“额外占用的内存空间”(不包括输入数据本身),比如临时数组、缓存、递归栈等。比如O(1)表示不管数据量多大,只占固定大小内存(比如用几个变量);O(n)表示占的空间和数据量成正比。
分场景根据需求搞优化
(1)先看时间复杂度:优先让算法“跑得快”
- 选对“工具”:不同算法差十万八千里
- 拆东墙补西墙:用空间换时间的经典操作
- 减少“无效劳动”:剪枝和提前终止
(2)再看空间复杂度:让内存“省着用”
- 能不用就不用:原地修改数据
- 榨干每一滴内存:压缩和分块
- 动态释放:用完就扔的“断舍离”
时间和空间复杂度就像程序员的“成本意识”——写代码琢磨怎么用最少的资源时间和空间把事儿办得最漂亮,硬件资源总是有限的不能总是把空间和时间消耗殆尽。
庄子言论 | 含义 | 《编程之道》 | 相似点 |
---|---|---|---|
“臣之所好者,道也,进乎技矣。始臣之解牛之时,所见无非牛者。三年之后,未尝见全牛也。方今之时,臣以神遇而不以目视,官知止而神欲行。”(《庄子·养生主》) | 我所追求的是道,已经超越技术层面了。刚开始我宰牛的时候,眼里看到的都是整头牛。三年之后,就不再把牛看成一个整体了。现在,我宰牛全凭精神去感知,而不用眼睛去看,感官的作用停止了,而精神在自由驰骋 。 | “当我开始编程时我看到的是整个一大块的程序,三年后我看到的是子过程。现在我什么也看不到了。我的整个存在是没有任何形式的虚无。我感觉很悠闲,总之,事实上是我的程序自己在写”(编程大师描述自己的编程境界变化) “那个程序员掌握了道。他不需要预先进行设计;系统崩溃时他也从不烦躁,只是接受发生的一切 。他不需要写文档,他从不顾及有没有人看他写的代码。他也不需要进行测试;他写的每个程序都有一个完美的自我,平静而优雅,它们的目的不言自明。他已经真正掌握了道的精髓。”(描述掌握道的程序员的状态) | 都强调从对事物表面的认知,经过学习和实践,达到对事物本质的深刻理解和把握,且能自然流畅地应对相关事务。在编程中体现为从关注程序整体到深入理解程序内部结构,最终达到无需刻意关注细节,程序自然生成的境界,与庖丁解牛从看到整头牛到看清牛的内部结构,再到凭精神解牛的过程相似 。 |
“吾生也有涯,而知也无涯 。以有涯随无涯,殆已!”(《庄子·养生主》) | 人的生命是有限的,而知识是无限的。用有限的生命去追求无限的知识,是很危险的。 | 未在文档中找到完全契合的原话,但书中体现出的思想是:编程领域知识技术不断更新,不应盲目追求掌握所有知识和技术,如在众多编程语言中,每种语言都有其用途,但没必要全部精通,应专注于核心的编程思想、算法、数据结构等内容 。 | 都提醒人们在面对无限的知识或事务时,要保持清醒的头脑,认识到自身的局限性,合理选择和分配精力。在编程上就是不要妄图掌握所有编程知识,而是有重点地学习 。 |
“至人无己,神人无功,圣人无名。”(《庄子·逍遥游》) | 修养最高的人能顺应自然、忘掉自己,修养达到神化不测境界的人无意于求功,有道德学问的圣人无意于求名。 | “我写程序是因为写程序很有趣,所以我并没想过得资金”(程序员拒绝经理分红,专注于编程本身) 文中多处体现出编程应专注于编程本身,追求程序的完美和谐,而非外在的名利,如编程大师专注于编程,不被外界干扰 。 | 都倡导一种超越个人功利的心态,专注于事物本身,追求更高层次的价值和境界。在编程中表现为不追求个人名利,而是专注于编写高质量、对用户有用的程序 。 |
“是亦彼也,彼亦是也。彼亦一是非,此亦一是非。”(《庄子·齐物论》) | 事物的这一面也就是事物的那一面,事物的那一面也就是事物的这一面。事物的那一面有它的是非标准,事物的这一面也有它的是非标准。 | “不论多么的微不足道,每种语言都有它自己的目的,每种语言都表达了软件的阴阳两极。每种语言都各得其道” “一个工作在微机上的程序员和大型机程序员都认为自己的系统有独特之处,且都乐于为其工作,最后两人成为至死的好友”(体现不同编程环境和技术都有其价值) | 都体现了一种相对主义的观点,看待事物不是绝对的、单一的,而是多元的、相对的,强调要全面、客观地看待不同事物。在编程中就是不同的编程语言、框架和开发方法都有其自身的特点和适用场景,没有绝对的好坏之分,应根据具体情况选择合适的技术 。 |