@zhongjianxin
2017-07-26T17:17:12.000000Z
字数 2692
阅读 1616
Trainning

本文的起源是因为思考一个问题,什么样的人适合做程序员。 我曾经苦恼得思考着这个问题,直到我在 SICP 上看到了答案。说的白话一点就是,能像机器一样思考的人就适合做程序员。
那么计算机这台机器是怎么思考的呢?这里是我的答案:我们所有的计算机,都是这个模型,江湖人称“冯・诺伊曼体系”
所有的问题都从输入和输出的角度去思考,这就是计算机这台机器的思考方式。也就是说你能做到这样思考,你就会像机器一样思考了。很简单吧,但是新的问题又产生了,处理自然要处理输入产生输出了。输入,输出是些什么呢?
机器在加工什么?SICP 中又说了,非形式的讲,我们只在处理两种东西,数据和过程,他们还不是严格区分的。
看我们的模型,中间处理的部分其实就是过程,输入和输出其实就是数据。(在冯诺伊曼体系里,数据和过程被称之为数据和指令)那说到数据,我们有一门学科叫做数据结构,它很好的表达了什么是数据。我们还有面向对象,类型系统之类的知识,他们都会帮助我们很好的定义数据。
下面我们拿几个例子来学习一下。我们来写一个加法函数,接受两个参数作为加数和被加数,返回一个和,这个太简单了,几乎任何一个程序员都可以在几秒钟内写完。拆成机器的思维是什么样呢?
加法函数
输入:
a
b
输出:
result
大概就长这样,输入是a和b,输出是一个结果,我们起名叫result。它到底表达了个啥样的代码呢?大概长这样:(本文所有的代码都会采用javascript描述)
function add(a, b) {
return a + b;
}
咦?result哪去了?在你调用的地方可能会有一行代码 var result = add(1,2);
这个表达方式不仅仅可以用来描述函数定义,用来描述表达式也是可以的。比如:
1. var result = a + b;
所以不仅仅可以用来描述函数定义,还可以描述代码块。。加上类型就变成了这样:
加法函数
输入:
a: Number
b: Number
输出:
result: Number
我们做一个稍微复杂的。比如下面这个:写一个函数,可以选出一个由数字组成的集合当中所有的偶数的最大值。这回一步做出来可能就有点难了,没关系,我们可以成两步:
1. 选出集合中的偶数
2. 选出偶数中的最大值
这两步呢,按照我们之前的格式写一下,大概是下面这个样子:
#1 选出集合中的偶数
输入:
inputArray
输出:
evenArray
#2 选出偶数中的最大值
输入:
evenArray
输出:
max:Number
集合怎么表达:[Number]就可以了。(我们的一个好习惯是一个集合里不要放两种类型的元素)
#1 选出集合中的偶数
输入:
inputArray: [Number]
输出:
evenArray: [Number]
#2 选出偶数中的最大值
输入:
evenArray
输出:
max: Number
咦,第二步的evenArray没有写类型。嗯,因为evenArray是第一步的输出,我就把它省了,相信大家也能看明白。
画图帮助思考:
以上思考过程还是展示了:
1. 分解问题
2. 找到子问题之间的关联(通过输入输出关联起来)
3. 找到问题的边界,明确假设与结果
上述三点看着简单,却是思维清楚与否的关键。我们管这个能力叫Analytical Thinking。
Tasking Practice:
1.自己试着做FizzBuzzWhizz的Tasking
2.写一个程序来计算一个文本文件 words.txt 中每个单词出现的频率。
为了保持简单,假设:words.txt 只包含小写字母和空格
每个单词只包含小写字母
单词之间由一个或多个空格分开
举个例子,假设 words.txt 包含以下内容:
the day is sunny the the
the sunny is is
你的程序应当输出如下,按频率倒序排序:
the 4
is 3
sunny 2
day 1

独立:{
意味着可拆分 职责单一 返例是多重循环 无法做到拆分彼此
所谓各自独立,说的就是在我们划分任务的过程中,每一个任务都对应一个代码块或一个函数,这些代码块和函数,是互相不包含的(不是不依赖,这是翻译的问题,各自独立的独立指的是Exclusive不是Independent)。
}
穷尽:{
每一个任务的输出都能被下一个任务输入使用
最终保证所有任务的配合完成可以完成这个目标
}
我们还是不太清楚怎么穷尽对吧。说是穷尽输入输出,到底输入输出都有多少大类呢?这个也是可以穷尽的。
我们的画图方法受时序图启发而发明,具体的规则如下:
1. 本图基本元素由方块和带箭头的线组成
2. 一个方块只代表一个函数或一个代码块,通常是函数,方块中可以写字,可以表达函数是属于哪个类或哪个实例等信息。
3. 指向方块的线代表该函数的输入,背离方块的线代表函数的输出。
4. 数据流动的时间轴遵守先从左到右,再从上到下的顺序。
5. 每一对输入输出(输入在上,输出在下)加一个方块,表达了一次函数调用。
画图可以有多种方式 输入输出的类型可以直接画在图中的箭头上
举例: 比如下列代码:
function c(){
}
function b(){
c();
}
function a(){
b();
}
a();
画成图是这个样子的
http://static.zybuluo.com/jtong/kjc93vf7lotu1uhl5682lhf7/a-b-c.png
example tasking:
#1 生成成绩单view model
输入:
studentIds: [String]
输出:
scoreSheet: {
studentScores:[{
name: String,
chinese: String,
english: String,
math: String,
programming: String,
average: String,
summary: String
}]
summary: {
totalAverage: Number,
totalMidden: Number
}
}
#2 打印成绩单
输入:
scoreSheet
输出:
result: String
PDCA(刻意练习):
Summary:
一个思维训练,对于这个思维训练,最重要的事情有三点:
1. 语文问题(用词精确,前后一致)
2. 接口问题(完全穷尽)
3. 每个函数之间互相不知道对方的内在实现(各自独立)
能做好这三点,代码就能完胜行业里的大多数人:)