<commit_before>title: How to Design Programs（前言）
tags:
- 译文
- Design Programs
---

## 前言

许多行亚都需要某种形式上的程序设计，会计编写电子表格；音乐家编写曲谱；作家编写文字；以及网络设计师编写样式表。当我们为这本书的第一版（1995-2000）写下这些文字的时候，人们正在考虑它们的未来；而现在，程序设计已经成为一项必要的技能，众多的网点在迎合这个需求——书籍、线上课程、K-12课程等，总是以提高人们的就业前景为目标。

这些典型的程序设计课程教授一种“不断修补，直到可以工作为止”的途径。当它可以工作时，学生大叫“它可以运行了！”然后再继续。可悲的是，那句话是计算机领域里最短的谎言，而且它已经花费了许多人生活中的许多时间。与之相反的是，这本书专注于让专业和职业的程序员养成好的编程习惯。

“良好的编程”在我们看来是以一种良好的途径来创造的程序，它依赖于系统化的思想，计划，并从一开始就认识到每一个阶段的每一步（应如何做）。为了强调这一点，我们会讲授系统化程序设计，以及系统化设计的程序。特别地，后者阐明了所期望的功能的基本原理。好的程序设计同时也能提高审美素养；良好的编程的优雅之处就像是历经时间考验的诗句或者是过去时代的黑白照片。简而言之，编程不同于好的编程，就像晚餐后的蜡笔画不同于博物馆的油画。

不，这本书不会让任何人变成一个油画大师。但是，如果我们不相信：“每个人都可以设计程序” 以及 ”每个人都能从有创意的设计中获得成就感。” 我们不会花费15年来写这个版本。实际上，我们甚至更进一步地对“程序设计——但不是**编程**”——应该像教授数学以及语言技能那样在文科中占一个席位进行争论。

一个学习了（程序）设计的学生，即使他此后再没有接触编程，他也依然掌握了普遍有用的解决问题的能力，进行深度创造性活动的经验，以及学习了欣赏新的审美意识。接下来的前言详细地解释了我们说的“系统化设计”这种方式带来的好处，以及我们会如何教授这一切。

<!-- more -->

---
## 系统化程序设计

程序与人（我们称之为用户）进行交互，彼此之间也进行交互，在这些情况下，我们通常称为服务组件和客户组件。因此任何合理的完备程序都可以认为是由许多构建模块构成：一些处理输入，一些创建输出，一些为这两者之间的界限构筑一个桥梁。我们选择使用函数来作为基本的构建模块，因为每个人都在预代数中遇到过函数，同时也因为最简单的程序就是函数。关键是发现哪些函数是需要的，如何连接它们，以及如何以基本元素来构建它们。

在这种情况下，“系统程序设计”指的是两个概念的混合：设计“食谱”（方法/处方）以及迭代求精。设计食谱是一个作者的创作；这里有一些他们之后能使用的（准则）

```
1. 以分析问题来进行数据定义
    认清在所选编程语言中必需要表示的信息并了解它是如何表示的，制定数据定义，同时用例子说明。
2. 签名，目的陈述，标题
    陈述所期望的功能是如何处理并产出数据的。用一条简介的单行声明语句准确地说出功能是如何运算的，为声明定义一个名副其实的存根
3. 应用实例
    通过一个可工作的实例说明功能的意图
4. 函数模板
    对函数的数据定义转译成函数大纲
5. 函数定义
    在函数模板中填补剩下的空白。利用目的声明以及例子（来进行）。
6. 测试
    用之前展示的实例来进行测试，并保证所有功能都通过测试。这样做能够发现错误同时也能在需要的时候帮助其他人阅读以及理解定义，这对于任何负责任的程序都是必需的。

图一： 函数食谱的基础设计步骤
```

**设计菜谱**应用于所有完整的程序以及各个函数中，这本书只处理两种完整程序中的食谱，一种GUI（图形用户界面）程序，另一种是批处理程序。与之相反的是，为各种功能设计食谱有各种各样的风格：有的用于形成原子类型的数据如数字，有的用于枚举不同类型数据，有的用于以固定的方式复合数据，有的用于有限但任意大小的数据，等等。

函数层级的食谱设计共用一套相同的设计流程。函数食谱基础设计步骤所展示的六个基础步骤。每一步的标题指定了预期的输出；那些指令给出了主要活动的建议。范例在每一个阶段都起到了核心作用。如步骤1中的选择数据表示，写下范例证明真实世界中的信息是如何作为数据被编码的以及数据是如何作为信息被解读的。步骤3说明了一个问题的解决必须要通过具体的应用场景来理解所需的函数在具体的例子里是如何运算的。这些理解都是为了步骤5中定义一个函数而进行铺垫的。最终，步骤6需要将范例变成自动化测试代码，用于保证函数在某些情况下正确运行。函数运行在真实世界的数据下可能会揭示一些预期结果与真实结果之间的差异。

设计流程的每一步都来源于关键问题。对于某些步骤，例如说：功能例子或者模板的创建时，会在数据定义时引出一些问题。这些问题的答案几乎自动地就创建出一个半成品，而这个脚手架在实现过程中需要迈出创造性的一步的时候给予我们回报：完成函数的定义。不仅如此，（设计流程）几乎所有情况下都会带来帮助。

这种方法的新奇之处在于创建初级程序的中间产物。当一个初学者遇到问题的时候，专家或者讲师可以查看现有的半成品。这个查阅的方式很可能是用一系列设计流程中的问题来引导初学者去修正他们自己的实现。而这个自我提升的过程就是编程与程序设计之间的关键区别。

**迭代细化**解决这个问题的难点是复杂以及多方面的。让所有事情一次就做对是几乎不可能的。相反，计算机科学借鉴了物理学的迭代细化（理论）来解决这一设计问题。事实上，迭代细化建议一开始就剥离所有琐碎的细节以及为剩下的核心问题寻找解决办法。每进行一次求精步骤就用尽可能使用现有的解决办法来忽略一些细节以及重新解决扩大了的问题域。每一次这些求精步骤的重复，也叫做迭代，会最终将我们导向问题的完整解决方案。

在这个场景下，程序员就是一个微科学家，科学家们为世界创造理想化版本的近似模型来进行预测。一旦这个模型的预测成为现实，那么没什么问题。当预期的事件与实际的不一样，科学家修正他们的模型来减少差异。同理，当程序员接到一个任务，他们创建第一个设计方案，将它转化成代码，让真实用户评价它并且反复提炼设计直到程序的行为足够接近预期所需的预期产物。

这本书用两种方法介绍了迭代细化（这一概念），当设计方案变得越复杂，精细化设计就变得越有用，当问题难到一定的程度时，这本书会在第四部分会明确地介绍这技术。此外在本书的第一、三两节课程里面，我们使用迭代细化来声明日益复杂的相同问题的变体。也就是说，我们选择一个问题，在一章中解决它们，然后在随后的章节中提出相似的问题——与细节相匹配的新引入的概念。

---

## DrRacket（编程语言）和教学语言

学习程序设计要求不断地动手练习。没有人能够不创造一个实际的程序并让它正确地工作就成为一个程序设计师，就像没有人能够不弹钢琴就成为钢琴家一样，因此，这本书自带一点小小的软件支持：一门用来写程序的语言和一个带有像word那样的文档编辑器，在实际输入上跑的程序开发环境。

人们经常告诉我们说他们希望自己知道怎么编程，然后又问我们他们应该选哪一种程序语言来学。鉴于通过资讯来了解某些语言而提出这样的疑问就不那么意外了。但这也是完全不恰当的。用当前时尚的编程语言来学习编程，常会让学生感到失败。时尚在当前世界当中是及其短暂的。典型的“快速用X语言开发”的书籍或者课程未能正确教导转换到下一个流行语言的原理。更糟糕的是，这些语言常常让人从获得转换原理中分心，包括寻求相应水平的解决方案以及处理程序错误。

与之相反的是，学习程序设计主要包括学习原理以及获得语言间转换的能力，理想的编程语言应该要对这两个目的支持良好，但现成的工业语言没有这样做。关键的问题是，初学者在他们十分了解这个语言之前就会犯错误，而程序语言总是将编程的人当作已经了解了整个编程语言来诊断错误。最终，诊断结果常常会卡住初学者。

我们的解决办法是从我们自己开始，量身定做一门语言，又称为“初学者语言”或者*SL。这门语言实际上是一门“外”语，它要求初学者学习过预代数的课程，它包括的符号有函数定义、函数应用、以及条件判断。同时，表达式可以嵌套，换句话说，这门语言是那么的小以至于对整个语言来说诊断报告中除了预代数来说依然是可读。

说到编程环境，我们遇到跟编程语言一样糟糕的选择，编程环境对于专业人士来说就像是喷气客机的座舱。它有众多的控制器以及显示屏，（能够）压倒任何第一次启动这样的程序的人。初学者需要能让他们练习基础技能的相当于双座，单引擎螺旋桨的飞机。因此我们为初学者创造了DrRacket语言以及一个编程环境。

DrRacket是一个以两个简单的互动窗格来支持非常好玩的面向结果为导向的语言：一个含有函数定义的区域以及一个允许编程者求证可能跟定义相关的表达式的结果。在这上下文中探索假设的场景跟在电子表格应用中一样容易。第一次接触就能用常规的计算表达式开始实验，而且很快就能继续计算图片、文字、以及其他格式的数据

一个像DrRacket这样的交互式程序开发环境将学习过程简化为两个途径。第一，它允许初学者直接操作数据。因为没有必要从文件或者设备中读取输入信息。初学者无需花费宝贵的时间来弄明白它是怎么工作的。第二，（我们）严格区分从真实世界中输入输出的数据和数据操作。今天，这种区分对程序系统化设计是那么的重要，以至于得到一个命名：模型-试图-控制器结构。通过用DrRacket，新手程序员从一开始就自然地直接接触这些基本的软件工程思想。

---

## 技能转移<commit_msg>翻译部分技能转译小节<commit_after>title: How to Design Programs（前言）
tags:
- 译文
- Design Programs
---

## 前言

许多行亚都需要某种形式上的程序设计，会计编写电子表格；音乐家编写曲谱；作家编写文字；以及网络设计师编写样式表。当我们为这本书的第一版（1995-2000）写下这些文字的时候，人们正在考虑它们的未来；而现在，程序设计已经成为一项必要的技能，众多的网点在迎合这个需求——书籍、线上课程、K-12课程等，总是以提高人们的就业前景为目标。

这些典型的程序设计课程教授一种“不断修补，直到可以工作为止”的途径。当它可以工作时，学生大叫“它可以运行了！”然后再继续。可悲的是，那句话是计算机领域里最短的谎言，而且它已经花费了许多人生活中的许多时间。与之相反的是，这本书专注于让专业和职业的程序员养成好的编程习惯。

“良好的编程”在我们看来是以一种良好的途径来创造的程序，它依赖于系统化的思想，计划，并从一开始就认识到每一个阶段的每一步（应如何做）。为了强调这一点，我们会讲授系统化程序设计，以及系统化设计的程序。特别地，后者阐明了所期望的功能的基本原理。好的程序设计同时也能提高审美素养；良好的编程的优雅之处就像是历经时间考验的诗句或者是过去时代的黑白照片。简而言之，编程不同于好的编程，就像晚餐后的蜡笔画不同于博物馆的油画。

不，这本书不会让任何人变成一个油画大师。但是，如果我们不相信：“每个人都可以设计程序” 以及 ”每个人都能从有创意的设计中获得成就感。” 我们不会花费15年来写这个版本。实际上，我们甚至更进一步地对“程序设计——但不是**编程**”——应该像教授数学以及语言技能那样在文科中占一个席位进行争论。

一个学习了（程序）设计的学生，即使他此后再没有接触编程，他也依然掌握了普遍有用的解决问题的能力，进行深度创造性活动的经验，以及学习了欣赏新的审美意识。接下来的前言详细地解释了我们说的“系统化设计”这种方式带来的好处，以及我们会如何教授这一切。

<!-- more -->

---
## 系统化程序设计

程序与人（我们称之为用户）进行交互，彼此之间也进行交互，在这些情况下，我们通常称为服务组件和客户组件。因此任何合理的完备程序都可以认为是由许多构建模块构成：一些处理输入，一些创建输出，一些为这两者之间的界限构筑一个桥梁。我们选择使用函数来作为基本的构建模块，因为每个人都在预代数中遇到过函数，同时也因为最简单的程序就是函数。关键是发现哪些函数是需要的，如何连接它们，以及如何以基本元素来构建它们。

在这种情况下，“系统程序设计”指的是两个概念的混合：设计“食谱”（方法/处方）以及迭代求精。设计食谱是一个作者的创作；这里有一些他们之后能使用的（准则）

```
1. 以分析问题来进行数据定义
    认清在所选编程语言中必需要表示的信息并了解它是如何表示的，制定数据定义，同时用例子说明。
2. 签名，目的陈述，标题
    陈述所期望的功能是如何处理并产出数据的。用一条简介的单行声明语句准确地说出功能是如何运算的，为声明定义一个名副其实的存根
3. 应用实例
    通过一个可工作的实例说明功能的意图
4. 函数模板
    对函数的数据定义转译成函数大纲
5. 函数定义
    在函数模板中填补剩下的空白。利用目的声明以及例子（来进行）。
6. 测试
    用之前展示的实例来进行测试，并保证所有功能都通过测试。这样做能够发现错误同时也能在需要的时候帮助其他人阅读以及理解定义，这对于任何负责任的程序都是必需的。

图一： 函数食谱的基础设计步骤
```

**设计菜谱**应用于所有完整的程序以及各个函数中，这本书只处理两种完整程序中的食谱，一种GUI（图形用户界面）程序，另一种是批处理程序。与之相反的是，为各种功能设计食谱有各种各样的风格：有的用于形成原子类型的数据如数字，有的用于枚举不同类型数据，有的用于以固定的方式复合数据，有的用于有限但任意大小的数据，等等。

函数层级的食谱设计共用一套相同的设计流程。函数食谱基础设计步骤所展示的六个基础步骤。每一步的标题指定了预期的输出；那些指令给出了主要活动的建议。范例在每一个阶段都起到了核心作用。如步骤1中的选择数据表示，写下范例证明真实世界中的信息是如何作为数据被编码的以及数据是如何作为信息被解读的。步骤3说明了一个问题的解决必须要通过具体的应用场景来理解所需的函数在具体的例子里是如何运算的。这些理解都是为了步骤5中定义一个函数而进行铺垫的。最终，步骤6需要将范例变成自动化测试代码，用于保证函数在某些情况下正确运行。函数运行在真实世界的数据下可能会揭示一些预期结果与真实结果之间的差异。

设计流程的每一步都来源于关键问题。对于某些步骤，例如说：功能例子或者模板的创建时，会在数据定义时引出一些问题。这些问题的答案几乎自动地就创建出一个半成品，而这个脚手架在实现过程中需要迈出创造性的一步的时候给予我们回报：完成函数的定义。不仅如此，（设计流程）几乎所有情况下都会带来帮助。

这种方法的新奇之处在于创建初级程序的中间产物。当一个初学者遇到问题的时候，专家或者讲师可以查看现有的半成品。这个查阅的方式很可能是用一系列设计流程中的问题来引导初学者去修正他们自己的实现。而这个自我提升的过程就是编程与程序设计之间的关键区别。

**迭代细化**解决这个问题的难点是复杂以及多方面的。让所有事情一次就做对是几乎不可能的。相反，计算机科学借鉴了物理学的迭代细化（理论）来解决这一设计问题。事实上，迭代细化建议一开始就剥离所有琐碎的细节以及为剩下的核心问题寻找解决办法。每进行一次求精步骤就用尽可能使用现有的解决办法来忽略一些细节以及重新解决扩大了的问题域。每一次这些求精步骤的重复，也叫做迭代，会最终将我们导向问题的完整解决方案。

在这个场景下，程序员就是一个微科学家，科学家们为世界创造理想化版本的近似模型来进行预测。一旦这个模型的预测成为现实，那么没什么问题。当预期的事件与实际的不一样，科学家修正他们的模型来减少差异。同理，当程序员接到一个任务，他们创建第一个设计方案，将它转化成代码，让真实用户评价它并且反复提炼设计直到程序的行为足够接近预期所需的预期产物。

这本书用两种方法介绍了迭代细化（这一概念），当设计方案变得越复杂，精细化设计就变得越有用，当问题难到一定的程度时，这本书会在第四部分会明确地介绍这技术。此外在本书的第一、三两节课程里面，我们使用迭代细化来声明日益复杂的相同问题的变体。也就是说，我们选择一个问题，在一章中解决它们，然后在随后的章节中提出相似的问题——与细节相匹配的新引入的概念。

---

## DrRacket（编程语言）和教学语言

学习程序设计要求不断地动手练习。没有人能够不创造一个实际的程序并让它正确地工作就成为一个程序设计师，就像没有人能够不弹钢琴就成为钢琴家一样，因此，这本书自带一点小小的软件支持：一门用来写程序的语言和一个带有像word那样的文档编辑器，在实际输入上跑的程序开发环境。

人们经常告诉我们说他们希望自己知道怎么编程，然后又问我们他们应该选哪一种程序语言来学。鉴于通过资讯来了解某些语言而提出这样的疑问就不那么意外了。但这也是完全不恰当的。用当前时尚的编程语言来学习编程，常会让学生感到失败。时尚在当前世界当中是及其短暂的。典型的“快速用X语言开发”的书籍或者课程未能正确教导转换到下一个流行语言的原理。更糟糕的是，这些语言常常让人从获得转换原理中分心，包括寻求相应水平的解决方案以及处理程序错误。

与之相反的是，学习程序设计主要包括学习原理以及获得语言间转换的能力，理想的编程语言应该要对这两个目的支持良好，但现成的工业语言没有这样做。关键的问题是，初学者在他们十分了解这个语言之前就会犯错误，而程序语言总是将编程的人当作已经了解了整个编程语言来诊断错误。最终，诊断结果常常会卡住初学者。

我们的解决办法是从我们自己开始，量身定做一门语言，又称为“初学者语言”或者*SL。这门语言实际上是一门“外”语，它要求初学者学习过预代数的课程，它包括的符号有函数定义、函数应用、以及条件判断。同时，表达式可以嵌套，换句话说，这门语言是那么的小以至于对整个语言来说诊断报告中除了预代数来说依然是可读。

说到编程环境，我们遇到跟编程语言一样糟糕的选择，编程环境对于专业人士来说就像是喷气客机的座舱。它有众多的控制器以及显示屏，（能够）压倒任何第一次启动这样的程序的人。初学者需要能让他们练习基础技能的相当于双座，单引擎螺旋桨的飞机。因此我们为初学者创造了DrRacket语言以及一个编程环境。

DrRacket是一个以两个简单的互动窗格来支持非常好玩的面向结果为导向的语言：一个含有函数定义的区域以及一个允许编程者求证可能跟定义相关的表达式的结果。在这上下文中探索假设的场景跟在电子表格应用中一样容易。第一次接触就能用常规的计算表达式开始实验，而且很快就能继续计算图片、文字、以及其他格式的数据

一个像DrRacket这样的交互式程序开发环境将学习过程简化为两个途径。第一，它允许初学者直接操作数据。因为没有必要从文件或者设备中读取输入信息。初学者无需花费宝贵的时间来弄明白它是怎么工作的。第二，（我们）严格区分从真实世界中输入输出的数据和数据操作。今天，这种区分对程序系统化设计是那么的重要，以至于得到一个命名：模型-试图-控制器结构。通过用DrRacket，新手程序员从一开始就自然地直接接触这些基本的软件工程思想。

---

## 技能转移

技能需要通过在两个方向上学习系统化的程序设计之间的转换而习得。自然地，它们也适用于一般的编程，编写电子表格程序，合成器，样式表，甚至是文字处理器。通过我们的观察表明，从图一得到的设计过程几乎能延展到任何程序语言（当中），它能应用于10行的程序，也能应用于10,000行的程序。通过该设计过程能够反射出程序的语言的编写形式以及编程问题的规模；但是这在很多方面都是值得的，一旦这个过程成为习惯。

学习程序设计也意味着获得两种普遍有用的技能。程序设计无疑会教授与数学一样的分析技能，特别地，（预）代数以及几何学。但是其中不像数学的是，编写程序是一项积极的学习方式。创建程序能够提供即时的回馈同时引导我们探索，实验，以及自我评估。结果得出的往往是互动的产品，与在练习本上练习相比这大大提升了成就感。

除了提高学生们的数学技能外，程序设计教授分析阅读与写作的技能，即使是以最小的设计任务为题，如果没有坚实的阅读能力，也是不可能设计程序来解决这些具有合理复杂度的问题的。反过来，程序设计方法迫使创建者他/她用正确且精确的语言说出想法。的确，如果学生真的吸收了设计方法，他们增强的关键技能比其他什么都要多。

要说明这一点，先看一下图一。根据流程说明，设计者必须
1. 分析问题陈述，通常是文字的陈述；
2. 提取并表达它的本质，抽象且带有例子；
3. 根据分析制作大纲以及计划；
4. 将结果与预期的结果进行评估；
5. 修正作品中错误检查以及测试暴露出来的问题；
{'fraction_non_alphanumeric': 0.08715755403999538, 'fraction_numerical': 0.006473240087851115, 'mean_word_length': 24.98198198198198, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7145517', 'n_tokens_mistral': 8582, 'n_tokens_neox': 10345, 'n_words': 129}