第九章 高级语言
导读
在前面的章节中,我们从硬件基础开始,逐步构建了计算机的完整软件栈:机器语言、汇编语言和虚拟机。本章将进入软件栈的最高层——高级编程语言。
高级语言提供了比机器语言和汇编语言更高层次的抽象,使程序员能够以更接近人类思维的方式编写程序。通过本章的学习,你将了解高级语言的发展历程,掌握面向对象编程的基本概念,并学习Jack语言——一种简单但功能完整的面向对象语言。
Jack语言的设计目标是教学性:它足够简单,可以在一章内介绍完毕,同时又足够完整,可以展示现代编程语言的核心特性。通过学习Jack,你将理解高级语言的基本构造,为后续构建编译器奠定基础。
核心概念详解
9.1 高级语言概述
高级语言是相对于机器语言和汇编语言而言的编程语言,提供了更高层次的抽象。
高级语言的特点
抽象性:提供数据类型、控制结构等高级抽象
可读性:使用接近自然语言的语法
可移植性:不依赖特定硬件架构
生产力:提高编程效率
高级语言的分类
过程式语言:
- 以过程(函数)为中心
- 代表:C, Pascal, Fortran
面向对象语言:
- 以对象为中心
- 代表:Java, C++, Python
函数式语言:
- 以函数为中心
- 代表:Haskell, Lisp, Erlang
脚本语言:
- 解释执行,动态类型
- 代表:JavaScript, Ruby, Perl
9.2 面向对象编程
面向对象编程(OOP)是现代编程语言的主流范式。
OOP的核心概念
封装(Encapsulation):
- 将数据和操作数据的方法绑定在一起
- 隐藏内部实现细节
- 通过接口与外界交互
继承(Inheritance):
- 子类可以继承父类的属性和方法
- 支持代码重用
- 建立类之间的层次关系
多态(Polymorphism):
- 同一操作作用于不同对象可以有不同的行为
- 支持接口重用
- 提高代码灵活性
类和对象
类(Class):
- 对象的模板或蓝图
- 定义对象的属性和方法
- 是抽象的数据类型
对象(Object):
- 类的实例
- 具有状态(属性值)和行为(方法)
- 在内存中占据空间
9.3 Jack语言概述
Jack是Nand2Tetris课程中使用的教学语言,具有以下特点:
语言特性
面向对象:支持类、对象、方法
命令式:使用赋值、条件、循环等控制结构
静态类型:变量和表达式有明确的类型
编译型:通过编译器转换为VM代码
Jack的基本结构
Jack程序由一个或多个类组成:
class ClassName {
// 静态变量
static type name;
// 构造函数
constructor ClassName new() {
// 初始化代码
}
// 方法
method returnType name(parameters) {
// 局部变量
var type name;
// 语句
let statement;
if statement;
while statement;
do statement;
return statement;
}
}9.4 Jack数据类型
Jack支持以下数据类型:
基本类型
int:
- 16位整数
- 范围:-32768到32767
- 支持算术运算
boolean:
- 布尔值:true或false
- 支持逻辑运算
char:
- 16位Unicode字符
- 用单引号表示:'a', 'B', '\n'
引用类型
String:
- 字符串类
- 字符序列
- 支持字符串操作
Array:
- 数组类
- 固定大小的元素序列
- 通过索引访问
对象:
- 用户定义的类实例
- 通过引用访问
9.5 Jack变量声明
Jack中的变量必须在声明后才能使用。
变量类型
静态变量(static):
- 属于类,不属于对象
- 所有对象共享
- 在类加载时分配
字段变量(field):
- 属于对象实例
- 每个对象有自己的副本
- 在对象创建时分配
局部变量(var):
- 属于方法
- 在方法调用时分配
- 方法返回时释放
参数(parameter):
- 方法的输入
- 在方法调用时分配
- 方法返回时释放
声明语法
class Example {
// 静态变量
static int count;
static String name;
// 字段变量
field int x, y;
field boolean active;
constructor Example new(int a, int b) {
// 局部变量
var int temp;
var boolean flag;
let x = a;
let y = b;
return this;
}
method void doSomething(int param1, boolean param2) {
var int localVar;
// 方法体
}
}9.6 Jack表达式
表达式由操作数、运算符和函数调用组成。
运算符
算术运算符:
+:加法-:减法*:乘法/:除法%:取模(通过Math库实现)
逻辑运算符:
&:逻辑与|:逻辑或~:逻辑非
关系运算符:
=:等于!=:不等于(通过组合实现)<:小于>:大于<=:小于等于>=:大于等于
表达式语法
// 简单表达式
let x = 5;
let y = x + 3;
let z = (x + y) * 2;
// 布尔表达式
let flag = (x > 0) & (y < 10);
let result = ~(a | b);
// 函数调用
let len = String.length(str);
let arr = Array.new(10);9.7 Jack语句
Jack支持以下几种语句:
赋值语句
let variable = expression;
let array[index] = expression;示例:
let x = 5;
let arr[0] = x + 1;
let obj.field = value;if语句
if (condition) {
// 语句块
} else {
// 语句块
}示例:
if (x > 0) {
let y = x;
} else {
let y = -x;
}while语句
while (condition) {
// 语句块
}示例:
var int i;
let i = 0;
while (i < 10) {
let arr[i] = i * 2;
let i = i + 1;
}do语句
do expression;用于调用返回void的函数或方法:
do Output.printString("Hello");
do Memory.deAlloc(obj);return语句
return expression; // 有返回值
return; // 无返回值(void方法)9.8 Jack类结构
Jack程序由一个或多个类组成。
类的组成
class ClassName {
// 1. 静态变量声明
static type varName;
// 2. 字段变量声明
field type varName;
// 3. 构造函数
constructor ClassName new(parameters) {
// 分配对象
// 初始化字段
// 返回this
}
// 4. 方法
method returnType methodName(parameters) {
var type varName;
// 语句
}
// 5. 静态方法
function returnType functionName(parameters) {
var type varName;
// 语句
}
}构造函数
构造函数负责创建和初始化对象:
constructor Point new(int x, int y) {
field int x, y;
let x = x; // 这里的x是参数
let y = y;
return this;
}实际上,构造函数需要分配内存:
constructor Point new(int x, int y) {
do Memory.alloc(2); // 分配2个字
let x = x;
let y = y;
return this;
}9.9 Jack标准库
Jack提供了一些标准库类:
Math类
class Math {
function int abs(int x);
function int multiply(int x, int y);
function int divide(int x, int y);
function int min(int x, int y);
function int max(int x, int y);
function int sqrt(int x);
}String类
class String {
constructor String new(int maxLength);
method void dispose();
method int length();
method int charAt(int index);
method void setCharAt(int index, char c);
method void appendChar(char c);
method void eraseLastChar();
method int intValue();
method void setInt(int value);
}Array类
class Array {
constructor Array new(int size);
method void dispose();
}Output类
class Output {
function void moveCursor(int row, int col);
function void printChar(char c);
function void printString(String s);
function void printInt(int i);
function void println();
function void backspace();
}Keyboard类
class Keyboard {
function char keyPressed();
function char readChar();
function String readLine(String message);
function int readInt(String message);
}Screen类
class Screen {
function void clearScreen();
function void setColor(boolean color);
function void drawPixel(int row, int col);
function void drawLine(int row1, int col1, int row2, int col2);
function void drawRectangle(int row1, int col1, int row2, int col2);
function void drawCircle(int row, int col, int radius);
}Memory类
class Memory {
function int peek(int address);
function void poke(int address, int value);
function Array alloc(int size);
function void deAlloc(Array obj);
}Sys类
class Sys {
function void init();
function void wait(int duration);
function void halt();
function void error(int errorCode);
}9.10 Jack程序示例
示例1:简单的Point类
class Point {
field int x, y;
static int pointCount;
constructor Point new(int ax, int ay) {
let x = ax;
let y = ay;
let pointCount = pointCount + 1;
return this;
}
method int getX() {
return x;
}
method int getY() {
return y;
}
method void setX(int newX) {
let x = newX;
}
method void setY(int newY) {
let y = newY;
}
method void move(int dx, int dy) {
let x = x + dx;
let y = y + dy;
}
function int getPointCount() {
return pointCount;
}
method void dispose() {
do Memory.deAlloc(this);
}
}示例2:使用Point类
class Main {
function void main() {
var Point p1, p2;
var int distance;
let p1 = Point.new(0, 0);
let p2 = Point.new(3, 4);
do p1.move(1, 1);
// 计算距离需要Math.sqrt
// 这里简化处理
do Output.printString("Point count: ");
do Output.printInt(Point.getPointCount());
do Output.println();
do p1.dispose();
do p2.dispose();
return;
}
}示例3:数组操作
class Main {
function void main() {
var Array arr;
var int i, sum;
let arr = Array.new(10);
let sum = 0;
let i = 0;
// 填充数组
while (i < 10) {
let arr[i] = i * i;
let i = i + 1;
}
// 计算总和
let i = 0;
while (i < 10) {
let sum = sum + arr[i];
let i = i + 1;
}
do Output.printString("Sum: ");
do Output.printInt(sum);
do Output.println();
do arr.dispose();
return;
}
}示例4:字符串处理
class Main {
function void main() {
var String s;
var int len;
let s = String.new(20);
do s.appendChar('H');
do s.appendChar('e');
do s.appendChar('l');
do s.appendChar('l');
do s.appendChar('o');
let len = s.length();
do Output.printString("String: ");
do Output.printString(s);
do Output.println();
do Output.printString("Length: ");
do Output.printInt(len);
do Output.println();
do s.dispose();
return;
}
}9.11 Jack语言设计决策
Jack语言的设计体现了教学语言的权衡。
简化特性
没有继承:
- 简化语言设计
- 避免复杂的类型系统
- 仍然展示OOP核心概念
没有异常处理:
- 简化编译器实现
- 避免复杂的控制流
- 使用错误码代替
没有垃圾回收:
- 手动内存管理
- 理解内存管理的重要性
- 简化运行时系统
保留特性
类和对象:
- 展示OOP核心概念
- 理解封装和抽象
方法调用:
- 展示消息传递
- 理解动态绑定
基本控制结构:
- if/else, while
- 展示命令式编程
重要知识点
知识点1:语言范式
不同的语言范式适合不同的问题域:
- 过程式:适合算法密集型应用
- 面向对象:适合大型复杂系统
- 函数式:适合并发和分布式系统
- 脚本:适合快速开发和原型设计
知识点2:类型系统
类型系统定义了语言的类型规则:
- 静态类型:编译时检查类型
- 动态类型:运行时检查类型
- 强类型:严格的类型转换规则
- 弱类型:宽松的类型转换规则
知识点3:内存管理
内存管理是语言设计的重要方面:
- 手动管理:程序员负责分配和释放
- 自动管理:垃圾回收器自动回收
- 引用计数:跟踪引用数量
- 区域管理:按区域分配和释放
知识点4:编译vs解释
编译型语言:
- 源代码编译为机器码
- 执行速度快
- 代表:C, C++, Rust
解释型语言:
- 源代码逐行解释执行
- 开发速度快
- 代表:Python, Ruby, JavaScript
混合型:
- 编译为中间代码,然后解释执行
- 结合两者优点
- 代表:Java, C#
知识点5:语言演进
编程语言在不断演进:
- 从过程式到面向对象
- 从静态类型到动态类型
- 从单线程到并发
- 从命令式到函数式
常见误区
误区1:认为高级语言比低级语言慢
实际上,现代编译器可以生成与手工编写的汇编代码一样快甚至更快的机器码。高级语言的生产力优势远大于可能的性能损失。
误区2:忽视内存管理
即使是高级语言,理解内存管理也很重要。内存泄漏、悬空指针等问题仍然存在。
误区3:认为OOP是万能的
OOP不是解决所有问题的银弹。对于某些问题,函数式或过程式方法可能更合适。
误区4:不理解语言设计权衡
每种语言设计都是权衡的结果。理解这些权衡有助于选择合适的语言。
误区5:认为学习一种语言就够了
不同语言有不同的优势和适用场景。学习多种语言可以拓宽视野,提高编程能力。
实践应用
实践1:实现简单的类
实现一个Rectangle类:
class Rectangle {
field int width, height;
constructor Rectangle new(int w, int h) {
let width = w;
let height = h;
return this;
}
method int getArea() {
return width * height;
}
method int getPerimeter() {
return 2 * (width + height);
}
method void scale(int factor) {
let width = width * factor;
let height = height * factor;
}
method void dispose() {
do Memory.deAlloc(this);
}
}实践2:实现链表
class List {
field int value;
field List next;
static List head;
constructor List new(int val) {
let value = val;
let next = List.nil();
return this;
}
function List nil() {
return List.nil(); // 需要特殊处理
}
method void insert(List newNode) {
let newNode.next = next;
let next = newNode;
}
method int getValue() {
return value;
}
method List getNext() {
return next;
}
method void dispose() {
if (~(next = nil)) {
do next.dispose();
}
do Memory.deAlloc(this);
}
}实践3:实现栈
class Stack {
field Array data;
field int size;
field int capacity;
constructor Stack new(int cap) {
let capacity = cap;
let size = 0;
let data = Array.new(cap);
return this;
}
method void push(int value) {
if (size < capacity) {
let data[size] = value;
let size = size + 1;
}
}
method int pop() {
var int result;
if (size > 0) {
let size = size - 1;
let result = data[size];
return result;
}
return 0; // 错误情况
}
method int peek() {
if (size > 0) {
return data[size - 1];
}
return 0;
}
method boolean isEmpty() {
return size = 0;
}
method int getSize() {
return size;
}
method void dispose() {
do data.dispose();
do Memory.deAlloc(this);
}
}实践4:实现简单的游戏
class PongGame {
field int ballX, ballY;
field int paddleX;
field boolean running;
constructor PongGame new() {
let ballX = 256;
let ballY = 256;
let paddleX = 200;
let running = true;
return this;
}
method void run() {
while (running) {
do moveBall();
do handleInput();
do draw();
do Sys.wait(50);
}
}
method void moveBall() {
// 简单的球移动逻辑
let ballX = ballX + 1;
if (ballX > 511) {
let ballX = 0;
}
}
method void handleInput() {
var char key;
let key = Keyboard.keyPressed();
if (key = 130) { // 左箭头
if (paddleX > 0) {
let paddleX = paddleX - 5;
}
}
if (key = 132) { // 右箭头
if (paddleX < 450) {
let paddleX = paddleX + 5;
}
}
}
method void draw() {
do Screen.clearScreen();
do Screen.drawCircle(ballX, ballY, 5);
do Screen.drawRectangle(paddleX, 250, paddleX + 60, 255);
}
method void dispose() {
do Memory.deAlloc(this);
}
}实践5:编写Main程序
class Main {
function void main() {
var PongGame game;
do Sys.init();
do Screen.clearScreen();
let game = PongGame.new();
do game.run();
do game.dispose();
do Output.printString("Game Over");
do Sys.halt();
return;
}
}本章小结
本章我们学习了高级编程语言的基本概念和Jack语言的具体语法。
核心要点回顾
高级语言:提供比机器语言更高层次的抽象,提高编程效率。
面向对象编程:以对象为中心,支持封装、继承和多态。
Jack语言:简单但完整的面向对象语言,适合教学。
数据类型:int、boolean、char基本类型,以及String、Array和对象引用类型。
程序结构:由类组成,包含静态变量、字段变量、构造函数和方法。
控制结构:if/else条件语句、while循环语句。
标准库:Math、String、Array、Output、Keyboard、Screen、Memory、Sys等类。
关键技能掌握
- 理解面向对象编程的基本概念
- 掌握Jack语言的语法规则
- 能够编写Jack程序
- 理解类和对象的关系
- 掌握Jack标准库的使用方法
与后续章节的联系
本章学习的Jack语言是后续章节的基础:
- 第十章将构建Jack编译器的前端,将Jack代码转换为VM代码
- 第十一章将完成编译器的后端,进行优化和代码生成
- 第十二章将实现Jack标准库,为Jack程序提供运行时支持
学习建议
动手实践:亲手编写Jack程序,加深理解
理解设计:思考Jack语言的设计决策和权衡
对比学习:将Jack与其他语言对比,理解异同
循序渐进:从简单程序开始,逐步增加复杂度
通过本章的学习,你已经掌握了高级语言的核心知识。从机器语言到高级语言,我们完成了一次重要的抽象跃迁。高级语言使编程变得更加容易和高效,是现代软件开发的基石。理解高级语言的工作原理,对于理解整个软件栈至关重要。