引子
写这篇文章的起因源于我和我老婆的一次讨论。上半年,她在学校里教Matlab。有一次我帮她解答一道上机的习题,大概用了50行代码左右。在解释完答案后,她就问我,“你一步一步是怎么想的?我好像就没有这种思维。”,这可把我问住了,因为那对我来说更接近于一种下意识的行动,很自然的就写出来了。这个回答她自然不满意了,那么这种思维到底是什么呢?
什么是程序
先让我们从什么是程序谈起。上大学时,估计大部分人都学过C语言,我清楚的记得谭浩强的那本书在开篇里提到,程序 = 数据结构 + 算法。从专业角度上来说,这个定义真是绝了,但相信对很多刚接触编程的人来说,会马上问什么是数据结构?算法又是什么呀? 所以在这里,我们先提出一个的通俗的解释。一个程序的作用就是接受输入,进行处理,然后产生输出,套用李笑来对写作的定义,程序也可以定义为:
程序 = 输入 + 处理 + 输出。
其中的处理代表着一系列有条理的操作,即流程化。
发现事物的流程
什么是流程化?
以生活中的炒菜为例,食材是输入,最后的菜品就是输出。而下厨过程中的洗菜、切菜(预处理),然后下锅、翻炒、调味进行加工(处理),最后出锅、装盘(后处理)就是这个处理的全过程。
如果这个例子让你觉得离编程世界太远了,下面我们再来分析下笑来老师多次提到过一个例子。他在写第一本书《托福词汇21天突破》时,是运用编程统计出了高频单词。按照我的理解,这个程序的输入是历年来的试卷的电子版,然后按照按照空格进行分词,接着统计每个单词出现的次数,排除常用的简单词汇,再把同一单词的不同形式(比如,如果是动词就有第三人称,过去时,完成时等多种形式,如果是名词,至少有单复数两种区分,还有相应的动词,形容词)出现的次数加起来作为这个单词的出现的总次数,最后按照这个统计次数的高低排序,输出的结果就是高频词汇集合了。
流程化是建立编程思维的基础,发现事物的流程,是开始编码的第一步。但只知道根据流程来编码,写出的代码估计会像流水账式的文章,有很多冗余,要想写出优雅的代码,下面一步就需要识别出流程中变化和不变的部分,并对变化的部分进行封装,常见的方式是提取函数。
识别变化与不变的部分
你是不是在想,咦,流程中还分变化和不变的部分?这怎么理解。
下面让我们接着聊炒菜这件事,以流程中的洗菜、切菜这一步为例,洗和切这两个动作不变的,菜是变化,因为我们不管不做什么菜,都需要先洗再切,但是每次的菜是可以不同呀,于是我们就可以封装菜这个变化的点,从而提取出两个函数:
函数名:洗
输入:菜
输出: 洗好的菜
函数名:切
输入:洗好的菜
输出:切好的菜
(如果你在想为什么不是一个函数?原因是单一职责原则啦,这个我们后面的文章中再讲)
顺着这个思路,我们再来看加调料这一步,炒一个菜,通常需要加不同的调料,比如说糖,醋,盐。每种调料需要加的多少也是不一样的,比如说20克,10克和5克。于是加调料这一步也可以封装成一个函数:
函数名:加调料
输入:1.调料名 2.重量
输出:添加指定重量的指定调料
再来笑来老师的计算高频词汇的程序,它有哪些变化的部分呢?请想想再看下文。这里我想到的一个点是统计单词出现次数的部分,统计的方法是一样的,不一样的是统计的单词,于是可以得到以下这样一个函数:
函数名:统计单词出现次数
输入:某个单词
输出:这个单词到目前为止出现的次数
当你能够准确识别出流程中变化和不变的部分时,你就可以得心应手的的写出百行左右的小程序。如果你要再进一步写出一个软件,那么需要的就是抽象能力了。
确定抽象与具体的界限
抽象这个词本身就很抽象,编程中到底需要抽象些什么呢?我的理解是抽象是指抽象实体或者职责。遗憾的是这个可能已经不属于入门的阶段要了解的知识了,我后续准备再补写章讨论这些地方。在这里,我只想提一点,在你开始运用抽象能力的时候,一定要注意确定抽象和具体之间的界限是否合适。
比如说,一个抽象的流程可以这样描述,一个任务可以通过预处理、处理和后处理三步完成。你会发现无论做香辣虾,统计词频都可以用这三步完成。做香辣虾时清洗大虾是预处理,下锅翻炒,调味是处理,摆盘算是后处理了。统计词频中按空格分词是预处理,统计每个单词出现的次数是处理,按出现次数大小排序就算后处理了。这样的抽象就属于抽象程序比较高的,它的优点是可以适配的诸多场景;缺点也很明显,太过抽象以至于只看这三步时,根本不知道要做什么。
而我们在前面指出的炒菜的流程,先是洗菜、切菜(预处理),然后下锅、翻炒、调味进行加工(处理),最后出锅、装盘(后处理)。相比来说就是更加具体的抽象了,因为它没有说明要做什么菜,但是做任何一道菜都需要这些步骤。
上述两种抽象的方法到底哪个好,则要结合具体的场景来分析。如果只是炒菜,那么第二个更好;如果是为处理任务提供一个通用的方法,第一个则要胜出了。所以在实际编程中,合理的确定抽象和具体之间的界限就是编程思维中最重要的一环。
总结
如果我们把程序定义为, 程序 = 输入 + 处理 +输出。那么编程思维,就是先发现事物的流程,然后识别变化与不变的部分,最后确定抽象和具体之间的界限。
自己是一名程序员,写关于编程方面的文章算是自己的本行了。这里先做下预告,本文是自己准备写的编程系列第一篇文章,陆续还将写编程新手进阶,编程高手展望等方面的文章,欢迎继续关注。