编程语言是人类和计算机沟通的桥梁,是一套系统,一种思维方式或一种哲学表达
虽然它们大都很抽象,但相比机器码而言已经可以被人类理解,使用和创造了\
语言的维度
编程语言的演进并非单一路径,也并非单一目标;大致可以分为以下三个维度
抽象维度
这是语言演进的核心主线之一,目标是“逐步向人类自然的思维方式和表达习惯靠拢”
光谱的两端:机器和人类
| 极端 | 核心视角 | 关注点 | 语言例子 |
|---|---|---|---|
| 机器端 | 如何指挥硬件 | CPU寄存器、内存地址、栈指针、指令周期 | 机器码、汇编语言 |
| 人类端 | 如何描述问题 | 业务逻辑、数学公式、数据关系、用户意图 | SQL, Python, 自然语言编程 |
演进
第一代:完全面向机器
- 机器语言:直接操作寄存器
- 这在70-80年代很常见,现在几乎不用了
- 查指令表,找到电脑CPU的指令手册
- 写程序,写一串二进制
- 输入,通过“前面板拨动开关”或现代调试器,把这串数字塞进内存的某个位置
- 运行,让CPU跳转到那个地址执行
- 汇编语言:助记符
- 汇编器把汇编语言转换成二进制
- 查指令格式、寻址模式、中断调用、寄存器用法
这个阶段,人在说机器的话
第二代:面向硬件操作,但开始封装
- C语言:巨大的飞跃。不需要再直接操作寄存器,可以写
a = b + c。编译器会帮你搞定如何把b和c从内存移到寄存器、做加法、再存回去 - 但是,你仍然需要思考”内存布局“、”指针“、”如何释放内存“。思维仍然被硬件结构(栈、堆)所限制
这个阶段,人在用机器能高效执行的”结构化“方式思考,但表达上已经接近数学了
第三代:面向人类操作,脱离硬件
- C++/Java/C#:引入了”类“、”对象“、”继承“。这是在用人类社会的”类“和”关系“去建模世界,让机器来模拟人类的概念
- Python/Ruby/Lua:更进一步,引入了动态类型、垃圾回收。当写下
list.append(5)时,完全不用考虑”数组空间够不够?要不要realloc?”。机器在后台默默帮你处理了繁琐的底层工作
这个阶段,语言开始主动迁就人的思维习惯
第四代及以后:面向特定问题,声明意图
- SQL:比如
SELECT name FROM users WHERE age > 18。只是在声明想要的结果,完全没有告诉计算机要怎么做。这是“趋近人类”的极致表现之一:只管要什么,不管怎么实现 - 函数式语言(Haskell, Lisp):写
sum = foldl (+) 0 list。这是数学上的“递归折叠”概念,非常抽象和高级 - 未来/实验性语言:用自然语言(英文、中文)写自然语句,或用图形拖拽编程
范式维度(思维模型)
- 命令式:先做A,再做B,再做C
- 声明式:我需要结果是X
- 函数式:用数学函数的组合和变换来处理数据
安全维度
Rust并不比C更“趋近人类语言”,它甚至更难学。但它在趋近“让编译器帮人检查内存错误”这个目标,这是另一个方向上的“趋近人类”——趋近于人类的“不犯错”和“省心”
工具论
很多人把语言当作工具,我认为这种工具论是片面的,这取决于你站在哪个角度看问题
- 从老板、项目经理的角度看这句话100%正确。编程语言就是为了交付产品、解决问题存在的工具
- 从语言设计者、计算机科学家的角度看这句话非常片面。编程语言是思维的载体、数学的衍生物、甚至是艺术
- 从普通程序员的角度看,这句话即是真理,也是陷阱
语言即世界观
如果编程语言只是工具,那么换一个工具应该只是手感问题
但现实中,语言深刻影响甚至限制了你解决问题的方式
这就是著名的Sapir-Whorf假说(语言相对论)在编程世界的体现
- 用C语言思考:要时刻想着“内存怎么管理”,“指针指向哪里”,“这块数据怎么布局”。产生的解决方案往往是硬件导向的
- 用Lua思考:会想“只需要一个Table,随便往里塞数据和函数就行”。解决方案往往是灵活、动态、适配导向的
- 用Rust思考:会想“这个数据的生命周期如何”,“所有权”,“编译器能否通过借用检查”。解决方案是安全与所有权导向的
比如解决一个同时修改共享数据的问题
- C++语境下:lock, Synchronized
- Rust语境下:从编译期就避免这种设计,或者使用
Mutex但配合所有权系统
语言并不是中立的。不同的语言就像不同的人,时刻面对着不同的环境,遇到相同的问题有着不同的反应
如果把编程语言仅仅看作工具,忽略了它以下几个重要的维度
它是一种思维训练
学习编程语言,其实是在学习如何精确、无歧义地描述一个过程。这种计算思维(分解、模式识别、抽象、算法)的价值远超语言本身
它是知识沉淀的载体
编程语言里封装了几十年甚至上百年的计算机科学智慧
- 垃圾回收(GC):自动管理内存,背后是图论和指针追踪算法
- 函数式编程(Map/Reduce):本后是lambda演算
- 泛型:背后是类型论
它是社区和文化的载体
- Python社区:强调”用一种最好、唯一的方式做一件事“(哲学)
- Ruby社区:强调”程序员要快乐,有多种方式“(美学)
- C++社区:强调”你不用的东西,不需要为其付出代价“(零成本抽象)
这些文化会影响你编码风格、沟通方式甚至价值观
工具论的积极作用
对于绝大数商业软件开发来说,工具论是工作层面的真理
老板关心的是功能能不能快速上线,成本多少,是否稳定;客户关心的是App卡不卡,会不会闪退
在这种语境下,用哪种编程语言,就是在选工具,目标是修好机器
把编程语言仅仅当作工具,会让你成为”码农“;而认识到语言是思维的延伸和知识的载体,才能让你向”工程师“或”计算机科学家“迈进
执行模型
很多时候会听到C/C++是编译型语言,Python是解释型语言
这让很多人误认为C是编译型语言,Python是解释型语言,但这其实是历史习惯说法,不是严格定义
需要明确:语言 =/ 必须用某种方式运行
编译型语言/解释型语言不属于语言本身的范畴,它们不是语言的“本质属性”,更是属于实现方式/运行模型/执行策略
C也可以被解释执行(比如用解释器),Python也可以被编译(比如编译成字节码.pyc,甚至用PyInstaller编译成可执行文件)
现代编程语言大都是混合体,几乎所有的现代语言都是“编译 + 解释 + JIT”的混合模型
| 语言 | 实际情况 |
|---|---|
| C/C++ | 编译为机器码(AOT) |
| Java | 编译 -> 字节码 -> JVM解释 + JIT |
| Python | 编译为字节码 -> 解释执行 |
| JavaScript | 解释 + JIT(比如V8) |
主流执行模型
- AOT编译(Ahead-of-Time)
- 解释执行(Interpreter)
- JIT编译(Just-In-Time)
- 混合执行(Hybrid VM)
- 源码转译(Transpilation)
- 字节码虚拟机模型(Bytecode VM)
- Trace JIT/自适应优化(Adaptive Execution)