为什么编程语言将永远存在
关于编程语言已死的论调,和打孔卡一样古老。用口语化的自然语言来描述一个包含数千个算法的软件应用程序,这个想法比计算机本身还要久远(参见1927年的电影《大都会》)。这是一种渴望——希望能告诉电脑你需要一百万美元,然后它们就会凭空变出来。你我都知道,编程并不是这样运作的。
但为什么编程语言会存在?为什么它们比口头的自然语言更优越?又是什么让它们具有不朽的特性?
"计算机变得像人类的危险,并不比人类变得像计算机的危险更大。" —— 康拉德·楚泽
1、编程语言到底做了什么
编程语言将数学公式、代数、算法和逻辑转化为电子现实。计算机,本质上是一台膨胀的自动计算器,将理论数学转化为任何人都可以在计算机上使用的现实世界应用程序。
#include <stdio.h>
// Takes birthYear and currentYear as parameters
// and returns the age
int calculateAge(int birthYear, int currentYear) {
return currentYear - birthYear;
}
int main() {
int birthYear = 1985;
int currentYear = 2026;
// Call the function and store the result
int age = calculateAge(birthYear, currentYear);
// Print the results
printf("Birth Year: %d\n", birthYear);
printf("Current Year: %d\n", currentYear);
printf("Age: %d\n", age);
return 0;
}
上面的C代码是一个非常简单的例子,通过提供出生年份和当前年份来计算某人的年龄。作为数学公式,这只是一个简单的减法。
使用C编程语言的程序员可以做基本数学公式做不到的事情:将公式转化为任何人都可以使用的可执行软件应用程序。
这个例子在技术上定义了年份都是整数(Integer)数据类型。如果用户假设年份是格里高利历中的年份,这种设计是没有问题的。但如果应用程序必须处理16位CPU上的全新世历法,它就会崩溃。与此同时,公式仍然是一样的。C代码还可以进一步使用计算机的内部组件,例如时钟,来定义变量。
#include <stdio.h>
#include <time.h>
// Function to calculate the age
int calculateAge(int birthYear, int currentYear) {
return currentYear - birthYear;
}
int main() {
int birthYear;
int currentYear;
// 1. Get the current year from the system
time_t t = time(NULL);
struct tm tm = *localtime(&t);
// tm_year represents years since 1900, so we must add 1900
currentYear = tm.tm_year + 1900;
// 2. Prompt the user for their birth year
printf("Enter your birth year (e.g., 1990): ");
scanf("%d", &birthYear);
// Optional: Basic validation to ensure
// the birth year isn't in the future
if (birthYear > currentYear) {
printf("Error: Birth year cannot be in the future.\n");
return 1; // Exit with an error code
}
// 3. Calculate and display the result
int age = calculateAge(birthYear, currentYear);
printf("\n--- Results ---\n");
printf("Current System Year: %d\n", currentYear);
printf("Calculated Age: %d\n", age);
return 0;
}
如果一个泰国人输入的生日不是1985年,而是2528年佛历*(泰国佛历)*,系统就会提示出生年份不能在未来。用户需要知道输入必须是格里高利历的年份。将C代码与数学公式进行比较,可以清楚地看到代码是如何将原始公式具体实现在计算机上的一个非常特定的算法中。这是一种在电子环境中的实现,随之而来的是它自身的各种后果。
任何没有技术知识的人都可以执行该软件,这一事实带来了社会挑战。程序员需要在处理地理位置、货币、时区、自然语言和日历时,意识到用户的社会背景。一个能够处理世界上所有货币的软件对于一个小型学校项目来说可能是过度设计,但一个不能处理不同时区的企业软件则是彻底的失败。需求往往不是由目标用户明确表达的,而是他们的社会背景和环境的结果。
2、欲望与代码之间的鸿沟
当社会谈论编程语言的终结时,他们通常指的是新型的编程工具。这些工具可以神奇地生成代码,而不需要程序员手动编写。早期的工具之一是Visual Basic 3.0,它采用了新颖的WYSIWYG("所见即所得")方法。这个词指的是它的用户界面设计器。在1993年,GUI设计器是一个绝对的新鲜事物。
如今,GUI设计器的概念在很大程度上已被淘汰,因为它们对可变屏幕尺寸缺乏灵活性。当每个人的屏幕分辨率都是800x600或1024x768像素时,GUI设计器是高效的。随着布局变得更加数据驱动和动态化,这种效率消失了,现在我们大多又回到了用代码编写GUI(例如SwiftUI或HTML/CSS)。
假设代码生成器接管了整个编程工作,在逻辑上是有缺陷的。生成器并不会取代编写计算机代码,而是通过生成大量代码来改变软件设计过程。这不仅包括GUI,还包括底层的算法本身。用户指示生成器编写一个特定的应用程序,然后生成器会产生所需的结果。用户的表达正是问题所在。
受情绪驱动且常常不理性的人类,要么过于模糊地表达他们的愿望(例如"我想做一个百万美元的应用"),要么没有为解决方案设计提供必要的背景(例如"自动收集我的发票")。程序员的首要工作,或者说软件工程师的首要工作,一直是通过采访用户来澄清和具体化他们真正需要什么来设计解决方案。有时候会达到让用户感到沮丧的程度("你为什么要问这么多问题?我已经告诉你我需要什么了!")。
"人们在看到产品之前不知道自己想要什么。这就是为什么我从不依赖市场研究。我们的任务是读懂尚未出现在纸上的东西。" —— 史蒂夫·乔布斯
用户很擅长表达他们想要什么,但完全无法表达他们实际上需要什么。当用户说"我需要一个关于xyz的数据库"时,可能简单的文件存储比一个完整的数据库管理系统是更好的解决方案。普通大众的伪技术术语很容易被误认为是具体的需求。程序员需要识别用户真正需要什么,并将这种需求转化为他们解决方案的每一个代码段。
3、性能与可维护性
软件不是一种"一次构建,永远运行"的产品,需要预算大量的维护成本。性能指标可能会随时间变化,而生成的代码在性能方面历来声名狼藉。无论使用什么类型的代码生成器,程序员总是需要对代码进行质量保证,以确保可维护性和合理的性能。
3.1 过时的API和延迟的更新
当你使用WYSIWYG、ORM或现代智能代理等代码生成器时,你会发现它们总是有点过时。这主要是因为它们内置了过时的信息。即使是现代的生成器和代理也需要时间来采用最新的更新。这些延迟的更新经常迫使程序员手动将代码调整到最新版本,如果他们希望保持最新状态的话。
package main
import "fmt"
func main() {
// Allocate memory (returns *int64)
ptr := new(int64)
// Assign the value
*ptr = 300
fmt.Printf("Value: %d, Pointer: %p\n", *ptr, ptr)
}
上面Go示例中的生成器(Gemini 3.1 Pro)不知道Go 1.26的最新更新可以简化整个代码。人类程序员会订阅接收编程语言的定期更新,并立即学习这些变化。下面的例子不是生成的,而是由人类编写的。它缺乏生成器的不必要冗余,并理解此上下文的输出要求。
package main
import "fmt"
func main() {
ptr := new(int64(300))
fmt.Printf("%p = %d\n", ptr, *ptr)
}
保持与最新API的同步并不是一个新现象。90年代后期的WYSIWYG HTML编辑器就已经面临着比浏览器花更长时间来采用最新支持的HTML功能的问题。2000年代初期的ORM也不断挣扎于跟上最新的数据库功能。如今的编码代理同样面临着识别最新API的挑战,导致生成过时的代码。
3.2 运行生成代码的成本
过去几十年来,许多研究考察了ORM(对象关系映射),发现与直接使用原生SQL或存储过程相比,存在严重的性能影响。
生成器不了解它们运行所在的执行环境,也不了解它们操作的实际生产用例和数据。这要求所有形式的生成器都采用通用的解决方案方法。这种通用方法的成本可能是人类程序员个性化方法的2到5倍。
在具有非常特定复杂访问模式的大型数据集、由数百用户执行的用例中,ORM几乎从经济角度来说是不合理的。像Dreamweaver或Microsoft FrontPage这样的WYSIWYG HTML编辑器理所当然地被程序员所鄙视,因为它们的代码包含大量冗余。这种冗余对TTFB产生了负面影响,从而在2000年代初期对网站产生了成本和收入方面的影响。
4、技术进步从未导致失业
现代社会中有一个神话,认为技术进步会导致失业和引起失业潮。这个神话从未在现实中实现过。直到1980年代,航空公司驾驶舱由3人机组操作,包括机长、副驾驶和飞行工程师。到1980年代初期,飞行工程师的职位消失了。飞机驾驶舱的技术进步使这个角色变得多余,并使今天的驾驶舱只需要两名机组人员。
当飞行工程师被取消后,人工成本显著下降。航空旅行随后猛增,并创造了大量对飞行员的需求。这是前飞行工程师非常胜任的工作。全球许多航空公司将他们的飞行工程师升级为副驾驶,以满足对飞行员的新需求。这是一个双赢的局面。
技术进步会自动化或消除任务,有时会消除整个角色。但它们从不消除对高技能人类劳动力的需求,而是创造对这种劳动力的新的、更大的需求。
4.1 真实的情况
代码生成器已经存在了几十年。它们的目标一直是为了让程序员更高效,而它们的制造商则声称现在每个人都可以编程,程序员将会过时。提高效率的说法经常被证明是正确的,而过时的说法从未成为现实。
计算机程序员的工作是为计算机编写指令并将其打包成软件包,以便其他人可以使用。了解一门编程语言以及如何使用它,只是专业程序员或软件工程师所具备的众多技能之一。
除了编程语言,程序员还需要了解计算机内部结构、外围设备和计算机网络的工作原理。这包括TCP/IP、UDP、无线以太网、显卡、鼠标、触摸屏等等。程序员知道如何从用户表达的模糊问题陈述中创建需求。他们知道如何让软件在数年甚至数十年内保持快速和可维护。计算机程序员理解数据结构和算法,因此他们可以为模糊描述的问题采用和调整具体的解决方案。
计算机程序员利用他们所有的知识,用编程语言编写软件应用程序。他们将"应用"打包成普通用户可以在他们期望的环境中使用的形式。今天的程序员不再发送成堆的打孔卡、软盘或CD-ROM,而是通过互联网分发他们的软件及其更新包。程序员的开发生命周期使用越来越多的工具来提高效率。这包括集成开发环境(IDE)、打包软件(例如2000年代初的InstallShield)、设计软件,当然还有各种代码生成器和助手。
工具始终让程序员更强大,从未让他们被淘汰。
如果你的孩子喉咙痛,含片不能缓解问题,那就去看专业医生。当你的汽车刹车不能正常工作时,你把它送到专业技师那里修理。当某人生成的无代码数据库因为索引不当而窒息时,他们会找专业程序员来调查和修复。业余爱好者能够自己做基本的事情,增加了专业人士的工作机会,而不是让他们被淘汰。
4.2 自然语言在算法方面的局限性
理论上,没有必要用C编程语言来编写计算机程序。人们可以用自然口语的英语来描述整个C代码。然而,当你看到它时,问题就立即显现出来了:它很难理解。
这个程序是一个简单的数字计算器,旨在通过与计算机内部时钟通信来确定一个人的年龄。它首先导入处理输入/输出和时间跟踪的基本工具,然后定义一个特定的数学规则:要找到年龄,必须从当前年份中减去出生年份。当程序运行时,它首先自动从系统的实时数据中检索当前日历年(将原始技术值加1900以匹配我们的现代日历)。然后暂停并要求用户输入他们的出生年份。在继续之前,代码执行逻辑检查以确保输入的年份不在未来;如果输入无效,它会停止并发出错误消息。如果输入有效,程序应用其减法规则,计算最终年龄,并整齐地打印当前年份和结果年龄。
当你在数学课上不得不解决应用题时,你知道它们可能有多么模棱两可。自然语言不是被设计出来的,而是随着人类在几个世纪中社会演化而来的。它们被人类用来表达情感、娱乐、教育和彼此交流。
软件不过是为确定性计算机器(大型计算器)编写的指令。自然语言可以用来表达这些指令。由于自然语言是通用语言,除非建立协议,否则它们的效率极低。这就是为什么军队使用特定的术语和协议——一套指令——来提高自然语言的效率。空中交通管制也是如此,它们也使用特定的、更高效的术语和协议。
# Example of Air Traffic Control (ATC) chatter
# highlighting the protocol and jargon
# in today's professional aviation
## Phase 1: Clearance Delivery
**Lufthansa 714 Heavy**:
Munich Delivery, good morning, Lufthansa 714 Heavy.
Boeing 787 at Stand 213, with Information Bravo,
requesting IFR clearance to Tokyo Haneda.
**Munich Delivery**:
Lufthansa 714 Heavy, Munich Delivery, good morning.
Cleared to Tokyo Haneda via the AKINI 4 Sierra departure,
flight planned route. Climb via SID, initial altitude 5,000.
Squawk 4321.
**Lufthansa 714 Heavy**:
Cleared to Tokyo Haneda, AKINI 4 Sierra departure,
flight planned route. Climb via SID to 5,000,
squawking 4321, Lufthansa 714 Heavy.
**Munich Delivery**:
Lufthansa 714 Heavy, readback is correct.
Contact Apron on 121.7 for push and start.
Have a safe flight.
**Lufthansa 714 Heavy**:
121.7 for push and start, thanks. Servus.
## Phase 2: Pushback and Engine Start
**Lufthansa 714 Heavy**:
Munich Apron, Lufthansa 714 Heavy, Stand 213.
Fully ready, requesting pushback and engine start.
**Munich Apron**:
Lufthansa 714 Heavy, Munich Apron.
Pushback and start approved, face east.
**Lufthansa 714 Heavy**:
Pushback and start approved, facing east.
Lufthansa 714 Heavy.
_(A few minutes later, after engines
are running and the tug is disconnected)_
**Lufthansa 714 Heavy**:
Apron, Lufthansa 714 Heavy, pushback complete, request taxi.
**Munich Apron**:
Lufthansa 714 Heavy, taxi via Outer 1 and Sierra 8
to holding point Runway 08 Right.
Contact Munich Ground on 121.9.
**Lufthansa 714 Heavy**:
Taxi via Outer 1, Sierra 8 to holding point 08 Right.
Over to Ground 121.9, Lufthansa 714 Heavy.
## Phase 3: Taxi to Runway
**Lufthansa 714 Heavy**:
Munich Ground, Lufthansa 714 Heavy,
taxiing Outer 1 approaching Sierra 8.
**Munich Ground**:
Lufthansa 714 Heavy, Munich Ground,
continue taxi to holding point Bravo 11,
Runway 08 Right. Monitor Tower on 118.8.
**Lufthansa 714 Heavy**:
Continue to Bravo 11, Runway 08 Right,
monitoring Tower 118.8. Lufthansa 714 Heavy.
## Phase 4: Takeoff Clearance
**Munich Tower**:
Lufthansa 714 Heavy, Munich Tower,
line up and wait Runway 08 Right.
**Lufthansa 714 Heavy**:
Line up and wait Runway 08 Right, Lufthansa 714 Heavy.
_(The 787 taxis onto the runway and aligns with the centerline)_
**Munich Tower**:
Lufthansa 714 Heavy, wind is 070 degrees at 12 knots.
Runway 08 Right, cleared for takeoff.
**Lufthansa 714 Heavy**:
Cleared for takeoff, Runway 08 Right, Lufthansa 714 Heavy.
## Phase 5: Airborne and Handoff to Departure
**Munich Tower**:
Lufthansa 714 Heavy, airborne at 42.
Contact Munich Departure on 123.9. Tschüss!
**Lufthansa 714 Heavy**:
Departure on 123.9, Lufthansa 714 Heavy. Tschüss!
_(The crew switches frequency to Munich Departure)_
**Lufthansa 714 Heavy**:
Munich Departure, good morning. Lufthansa 714 Heavy,
passing 2,500 climbing 5,000, AKINI 4 Sierra.
**Munich Departure**:
Lufthansa 714 Heavy, Munich Departure, radar contact.
Climb flight level 110. Proceed direct AKINI.
**Lufthansa 714 Heavy**:
Climb flight level 110, direct AKINI, Lufthansa 714 Heavy.
编程语言也不例外。它们是一种带有特定格式要求的英语术语,作为为计算机编写指令的协议。如果你想用"英语"编程,那么像Python或C这样的编程语言就是你能得到的最接近的东西。它们已经是英语了,带有必要的术语和协议,以保持尽可能安全和高效。然而,用自然口语的英语大声朗读Python代码是完全可能的。
### Example of how to read Python code aloud ###
# Define function "Calculate age"
# with parameters "Birth year" and "Current year"
def calculate_age(birth_year, current_year):
# Return the difference between
# "Current year" and "Birth year"
return current_year - birth_year
# Define function "main"
def main():
# set birth year variable to 1985
birth_year = 1985
# set current year variable to 2026
current_year = 2026
# Set age to the function result of "Calculate Age"
# passing "Birth Year" and "Current Year"
age = calculate_age(birth_year, current_year)
# Print birth year variable
# prefixed with "Birth Year:" to screen
print(f"Birth Year: {birth_year}")
# Print current year variable
# prefixed with "Current Year:" to screen
print(f"Current Year: {current_year}")
# Print age variable
# prefixed with "Age:" to screen
print(f"Age: {age}")
# Call the "main" function when
# the __name__ variable equals the
# text "__main__"
if __name__ == "__main__":
main()
在编程、航空、医疗手术、铁路等领域,专业的术语和协议每时每刻都被全球数百万专业人士学习、训练和应用。如果没有这些协议,这些专业人士的工作将极其低效,甚至可能危及生命。由于他们的术语和协议,专业人士可以在一个人谈论该领域的前几句话中立即识别出谁不具备他们的技能。
# Operation Room Transcript Log
# (Total Hip Arthroplasty)
_Lead Surgeon: Dr. Paula Mercer_
_Surgical Assistant/Scrub Tech: Sarah_
**[08:14:22] Dr. Mercer**
The surgical time-out is complete.
Let's get started. 10 blade, please.
**[08:14:25] Sarah**
10 blade to you.
**[08:14:35] Dr. Mercer**
Bovie. Let's maintain hemostasis as
we go down through the superficial fascia.
**[08:15:10] Dr. Mercer**
Okay, we're at the tensor fasciae latae.
I need a Charnley retractor to hold this open.
**[08:15:14] Sarah**
Charnley going in.
**[08:16:05] Dr. Mercer**
Good. Let's get a Hohmann retractor placed superior
to the capsule to give us a better view.
**[08:16:10] Sarah**
Passing the Hohmann.
**[08:18:30] Dr. Mercer**
The capsule is incised. Let's internally
rotate the leg to dislocate the hip. Sarah,
hold that leg steady and keep the traction firm.
**[08:18:35] Sarah**
Holding steady.
**[08:19:00] Dr. Mercer**
Hand me the oscillating saw, please.
I'm going to make the femoral neck cut.
**[08:19:05] Sarah**
Saw coming up. Irrigation is ready.
**[08:19:45] Dr. Mercer**
Perfect. Extract the femoral head. Corkscrew.
**[08:19:50] Sarah**
Corkscrew passed. Head is out.
**[08:22:12] Dr. Mercer**
Let's address the acetabulum first.
Hand me the 48-millimeter reamer.
**[08:22:18] Sarah**
48 reamer.
**[08:24:45] Dr. Mercer**
We need to go a bit wider to get to bleeding bone.
Size up. 50-millimeter reamer.
**[08:24:50] Sarah**
50 reamer.
**[08:26:30] Dr. Mercer**
That looks like a great fit. Let's get the
52-millimeter acetabular shell on the impactor
and hand me the heavy mallet.
科技和科学的进步使得普通大众能够进行非常基础的计算机编程、用业余无人机参与航空活动,以及在家里进行基本的急救。所有这些都发生在有限的业余范围内,而专业应用仍然留给专业人士。专业人士始终能够操作比业余爱好者更先进的技术。
假设计算机编程将会消失,仅仅因为更多业余爱好者能够编程,忽略了专业计算机程序员自身也已经显著进步的事实。专业技能不仅仅是做某事的物理能力,而是知道如何做、何时做、在哪里做什么的认知能力。
原文链接: Programming Is Linguistically Immortal, or Why Programming Languages Are Here to Stay
汇智网翻译整理,转载请标明出处