Nodejs周末随想:数据 vs 代码
Nodejs周末随想:数据 vs 代码
1. 程序 = 数据结构 + 算法
这是瑞士计算机科学家Niklaus Wirth写的一本书:Algorithms + Data Structures = Programs。这本书揭示了算法和数据结构之间的紧密关联关系。
Wirth是几种计算机编程语言的设计者,包括Pascal和Modula-2等。他获得了1984年的图灵奖。
从另外一个角度,这个句话也说明了程序就是代码(指令)作用于数据。
2. Arduino之哈佛体系结构
当前计算机的主流体系结构是冯诺依曼结构,其中一个很重要的概念是“存储程序”:代码和数据统一保存在内存中。由于冯诺依曼曾经在普林斯顿大学高等研究院工作,所以也叫做普林斯顿体系架构。
Arduino是一个开源的电子平台,创意工作者和设计人员用它制作好玩的交互硬件产品。为了测试Johnny Five框架,我特意买了一个Arduino UNO R3的板子以及一些周边硬件。在实验中,我发现Arduino采用的是哈佛体系结构:代码和数据分别使用不同的存储。具体来说用32KB Flash存储保存程序,用2K 静态内存保存程序处理的变量等,还有1K EEPROM持久性地保存某些数据。
3. OOP vs FP
Object无疑是面向对象程序设计(Object Oriented Programming,OOP)的核心概念,它把数据和对数据的操作(代码)封装在一起。OOP主要关注Object的组合。
Function是函数式编程(Functional Programming,FP)的核心概念。FP的函数有一个重要性质是:函数的输出仅仅与函数的输入参数相关,不会修改程序的全局状态。从这个意义上,我们可以把纯函数视为一段处理代码,数据流进函数,处理完后流出函数,即实现了数据和代码的分离。这种结构很适合并行计算和分布式处理的实现。FP主要关注Function的组合。
4. 轻量级虚拟机 vs Docker容器
其实容器(container)技术由来已久,如Solaris系统的Zones和FreeBSD的Jails。就是Linux系统,也早就有OpenVZ和LXC。那为什么大家都没有Docker这么声势浩大呢?不知道,也许是创始人或者投资者有很好的营销手段和广泛的人脉吧。
细分起来,OpenVZ/LXC与Docker创建的容器是不一样的。前者创建的是轻量级虚拟机,它们有自己的启动服务和配置,用户通常在一个容器中安装应用所需的应用服务器、数据库等等,相当于应用的数据(状态)和代码都封装在一台容器中。而Docker的容器每次没有启动服务,所以建议用户每个容器就运行一个无状态应用程序,数据(状态)是以数据卷的形式附加到容器上。也就是Docker容器把代码和数据分离了。
5. 结束语
- 冯诺依曼结构、OOP和轻量级虚拟机:封装了代码和数据;
- 哈佛体系结构、FP和Docker容器:分离了代码和数据。
看资料若有所思,暂且记下来,还请大家多指正。
Nodejs周末随想:数据 vs 代码
1. 程序 = 数据结构 + 算法
这是瑞士计算机科学家Niklaus Wirth在他的著作《Algorithms + Data Structures = Programs》中提出的一个观点。这本书强调了算法和数据结构之间的密切关系。Wirth不仅是Pascal和Modula-2等编程语言的设计者,还因此获得了1984年的图灵奖。
从另一个角度来看,程序本质上是代码(指令)对数据进行操作的结果。例如,在JavaScript中,我们可以定义一个简单的函数来处理数组数据:
// 定义一个处理数组的函数
function sumArray(arr) {
return arr.reduce((acc, curr) => acc + curr, 0);
}
const numbers = [1, 2, 3, 4, 5];
console.log(sumArray(numbers)); // 输出 15
在这个例子中,sumArray
函数是对数据 numbers
进行操作,体现了代码对数据的处理。
2. Arduino之哈佛体系结构
当前计算机普遍采用的是冯·诺依曼结构,其中代码和数据共存于同一内存空间。然而,Arduino采用的是哈佛体系结构,代码和数据分别存储在不同的存储器中。例如,Arduino UNO R3使用32KB的Flash存储程序,2KB的静态内存用于存储程序运行时的变量,1KB的EEPROM用于持久化存储数据。
// Arduino 示例代码
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT); // 初始化内置LED引脚
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // 点亮LED
delay(1000); // 延迟1秒
digitalWrite(LED_BUILTIN, LOW); // 关闭LED
delay(1000); // 延迟1秒
}
在这个例子中,代码和数据是分离的,程序代码存储在Flash中,变量和状态信息存储在静态内存中。
3. OOP vs FP
面向对象编程(OOP)的核心是对象,它将数据和对数据的操作封装在一起。例如,JavaScript中的类可以很好地体现这一点:
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
const rect = new Rectangle(10, 20);
console.log(rect.getArea()); // 输出 200
函数式编程(FP)则强调函数的纯度,函数的输出仅依赖于输入参数,不修改全局状态。例如:
function add(a, b) {
return a + b;
}
console.log(add(10, 20)); // 输出 30
在这个例子中,add
函数只处理输入参数 a
和 b
,不涉及任何外部状态,体现了数据和代码的分离。
4. 轻量级虚拟机 vs Docker容器
轻量级虚拟机(如OpenVZ和LXC)包含了自己的启动服务和配置,应用的状态和代码一起封装在同一个环境中。而Docker容器则更倾向于将代码和数据分离,每个容器只运行一个无状态的应用程序,数据以数据卷的形式附加到容器上。
# 创建Docker容器
docker run -d --name myapp -v /data:/app/data myimage:latest
在这个例子中,/data
目录被挂载为 /app/data
,数据和代码分离,便于管理和扩展。
结束语
- 冯·诺依曼结构、OOP和轻量级虚拟机:封装了代码和数据;
- 哈佛体系结构、FP和Docker容器:分离了代码和数据。
希望这些思考对你有所帮助,欢迎讨论和指正!
在 Node.js 的上下文中,“数据 vs 代码”的讨论同样具有重要意义。以下是一些相关的示例和解释:
1. OOP vs FP
OOP 示例
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
}
}
const user = new User('Alice', 25);
user.sayHello();
在这个例子中,User
类将数据 (name
, age
) 和操作这些数据的方法 (sayHello
) 封装在一起。
FP 示例
const sayHello = (name, age) => {
return `Hello, my name is ${name} and I am ${age} years old.`;
};
console.log(sayHello('Alice', 25));
在这个例子中,函数 sayHello
接收数据作为输入,并返回处理后的结果。函数的输出仅依赖于输入参数,不改变程序的全局状态。
2. 轻量级虚拟机 vs Docker 容器
在 Node.js 应用中,我们可以通过环境变量或配置文件来管理数据和代码的分离。例如:
环境变量
const express = require('express');
const app = express();
app.get('/', (req, res) => {
const data = process.env.MY_DATA;
res.send(`Data: ${data}`);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
在这个例子中,MY_DATA
环境变量用于存储应用的状态数据,而不是硬编码在代码中。
总结
Node.js 提供了多种方式来实现数据和代码的分离,通过函数式编程和配置管理,可以更好地管理和维护应用的状态。这样的做法有助于提高代码的可读性和可维护性,同时也便于并行计算和分布式处理的实现。