昨天我分享了一篇如何使用 PlantUML 这门简单的语言来绘制复杂的活动图、流程图和组件图等,有的同学看到了以后,询问我,“外行需要多长时间才能学会这玩意儿呀?”,这个问题不知道该如何回答,因为我并不认同“外行”这个词。我始终认为,在编程这件事情上,每个人都是内行,每个人也都是外行。事实上,从不了解 PlantUML 到能够参考文档绘制出复杂的流程图,我也就学了一个上午,边学习、边实践、边分享,于是就会了。


学会编程没有你想象中的那么复杂。


编程是什么?


编程,说的简单一点,就是通过一系列逻辑将你想做的事情或者想描述的物体表达清楚,然后让它展现出来,或者运动起来。说的专业一点:


编程 = 算法 + 数据结构


什么是算法?就是解决问题的办法,或者说通过几个步骤来解决一个问题的过程描述;那么什么是数据结构呢?咱们在解决问题的时候经常需要去放置一些物件,比如把书放到书架上,那么书架就是一种数据结构,把书放到柜子里,柜子就是一种数据结构,书架和柜子就是数据的不同呈现/储存方式。


其实,每个人对编程都不陌生,你进过厨房吧,17:00 回到家,怎么让家人在 18:30 之前吃上饭?这里头的算法就多了去了,你可以先煮上饭然后去买菜,也可以买完菜再回来煮饭,那么哪种方式更好呢?下面我们用程序语言来分析这道题:


编程问题: 17:00 回到家,怎么让家人在 18:30 之前吃上饭?
算法一:先煮上饭然后去买菜
算法二:买完菜再回来煮饭


这里的做饭是一个程序实体,它包含了煮饭、买菜、切菜、做菜,这个程序实体的表达方式是:


做饭 = {
  煮饭() {},
  买菜() {},
  切菜() {},
  做菜() {},
}


把中文换成英文不就是你平时看到的程序代码么?所以说呀,编程其实并不陌生,它也没你想象中的那么复杂。


编程的核心是什么?


为什么人跟人之间编写出来的代码有这么大的差异,或者说,为什么存在小白和专家的区别?编程确实不复杂,复杂的原因是很多人不能把问题思考周全,我举个例子你就知道了:


做饭 = {
  开始() {
    煮饭(); 买菜(); 切菜(); 做菜();
  },
  煮饭() {},
  买菜() {},
  切菜() {},
  做菜() {
    if (家里没有油了) { 买油(); 炒菜(); } 
    else { 炒菜(); }
  }
}

做饭->开始();


我们定义了一个程序实体叫做「做饭」,包含了几个步骤,开始、煮饭、买菜、切菜和做菜,在编程语言里头,我们把「做饭」称之为对象,这几个步骤称之为方法,「做饭」这个对象拥有 5 个方法,我们可以一个个地调用它。首先我们调用了「开始」方法,在这个方法里,又依次调用了「煮饭」、「买菜」、「切菜」和「做菜」。


在「做菜」方法里,我们看到了一个细节,那就是“家里没油了”,咋整,这个人是这么考虑的:先去「买油」,然后回来「炒菜」。很显然,这人不靠谱,你看,菜都要下锅了,才想起没有油。但是下面这个人就不一样了:


做饭 = {
  开始() {
    煮饭(); 
    检查结果 = 检查();
    买菜(检查结果); 切菜(); 做菜();
  },
  检查() {
    if (家里没有油了) { 买菜的时候要买油  } 
    if (家里没有辣椒了) { 买菜的时候要买辣椒  } 
  },
  煮饭() {},
  买菜() {},
  切菜() {},
  做菜() {},
}

做饭->开始();


他的程序里多了个步骤叫做「检查」,在出门买菜之前,先在家里扫一眼,缺了什么,用小本本记下来,然后「买菜」的时候,带上这个小本本,这样「买菜」就不会有遗漏了。


你看,这就是我们所谓的小白和专家,他们的区别就是后者能够把事情想得更加周全,在解决问题的时候,不遗留任何细节,并且呢,能够让事情可以更流畅、更快、更好地得到解决,消耗的资源最少,解决的问题最多。


编程的复杂性


不要以为我上面写的东西不是代码,稍微调整下细节,这串代码是可以在电脑上真实跑起来的,是不是特别简单啊?你还敢说自己不懂编程么?还会惧怕编程么?


但是也不要把编程想的太简单了,上面的程序表达的只是一个十分粗略的做饭过程,或者说一个做饭的思路,真正要把做饭的程序实现出来,还要考虑很多的问题,比如如何在程序中表达我要做辣椒炒肉、红烧狮子头、剁椒鱼头等等好几个菜呢?这里就涉及到“抽象”的概念,我们需要把很多相似的步骤都抽象成一种行为,然后不断重复这种行为:


做饭 = {
  开始(菜单) {
    煮饭();
    买菜(菜单);
    菜单->逐一(做菜);
  },
  买菜() {},
  煮饭() {},
  做菜(菜品) {
     洗菜(菜品);
     切菜(菜品);
     炒菜(菜品);
   }
}

菜单 = 辣椒炒肉、红烧狮子头、剁椒鱼头;
做饭->开始(菜单);


好了,上面的代码相信也不是很难理解,我们把做饭分为三个事情,「煮饭」、「买菜」和「做菜」,首先我们想好了一个“菜单,然后抽象了一个「做菜」的方法,这个方法里面包含了「洗菜」、「切菜」和「炒菜」三个步骤,每一道菜都会执行这三个步骤。


如果没有这层抽象会有什么问题?你会发现你的代码是这么写的:


做饭 = {
  煮饭() {},
  买菜() {},
  洗菜() {},
  切菜() {},
  做菜() {},
}

做饭->煮饭();
做饭->买菜();

做饭->洗菜(辣椒炒肉);
做饭->切菜(辣椒炒肉);
做饭->炒菜(辣椒炒肉);

做饭->洗菜(红烧狮子头);
做饭->切菜(红烧狮子头);
做饭->炒菜(红烧狮子头);

做饭->洗菜(剁椒鱼头);
做饭->切菜(剁椒鱼头);
做饭->炒菜(剁椒鱼头);


代码本身没有什么问题,但是看起来会十分冗长,如果你今天要做 10 个菜,那么代码就得写 10 遍;可如果你用到了抽象思维,你就只需要去扩展“菜单就行了,因为在程序里有一个叫做 「逐一」的逻辑。


程序里面涉及到的逻辑并不多,诸如「条件判断」、「循环」、「遍历/逐一」等,很少,但是也就是这么几个少量的逻辑,构筑了丰富多彩的网络世界。


小结


好了,本文并不是想教会你如何编程,而是想告诉你,编程是一件十分简单的事情,但是想写出好的程序却是一件无比有难度的事情,这需要你想出足够好的算法,同时也需要你对程序的执行环境有基本的了解,知道怎么写程序跑的快、怎么写程序会很卡,等等。


当然,作为程序员最苦恼的事情,并不是编程本身,而是需求的变化。比如当你做好了这顿饭,却发现家人在外面吃过了,此时的你就只能含着泪,一个人吃完这桌难以下咽的饭菜了。