你好,我是胡光。欢迎来到我的极客时间专栏。在接下来的两个多月里,我将陪伴在你的每一天的清晨或是夜晚,在人潮拥挤的上班地铁上,在你家里的书桌前,再或者是在你公司楼下的咖啡厅里,每天10分钟,让好学的你,有所收获,就是我的任务。

那些年,我学过的编程语言

面对编程这个话题,或许你已是一位编程老手,对编程熟悉无比,现在是想查缺补漏;亦或许你是一个纯新手,对编程一无所知,学习完全是从0开始。不管哪种情况,在我们讨论编程学习的时候,怎么都绕不开一个话题,那就是语言选择。

鉴于以往的工作经历,我了解或者熟悉的编程语言有十几种之多,包括:

此外,还有一些仅仅是使用过,能看懂的语言,就不列出来了。

你可能会有疑问了,为什么我会这么多编程语言呢?原因很简单,工作中是一个边学习边工作的过程,不同编程语言擅长做的事情不一样,让专业的语言干专业的事情,这是一个程序开发人员最基本的认知,所以我能学会多种编程语言也是情理之中。

你可能又会问了,学习了这么多编程语言,难道不会造成混淆么?其实,编程语言设计者,更多的还是为了让人们使用自己的编程语言进行开发,所以语言设计本身都会有前辈语言的主流特征,这也就是为什么,只要你学习了一门主流语言后,会大大降低你学习第二门语言的成本。可第一门语言的选择,是门技术活,这也是今天我们要讲的主题。

最后你是否好奇:“我为什么还要自己开发一门娱乐级编程语言呢?”简单点儿回答就是:自娱自乐。正式点儿的回答就是:经历了开发编程语言这个过程,会对很多语言的特性理解得更深刻,知其然,知其所以然。所以当你自己能开发出一门编程语言的时候,站在开发者的角度再去学习其他编程语言,简直就属于“降维打击”般的学习。

Pascal、C、Python,哪个是你的首选?

刚才列举了十多种编程语言,接下来我会以我的亲身经历,来说说学习不同语言都有什么样的体验,以及我在学习这些语言的过程中,遇到的惊喜和踩过的坑。

我从2006年开始接触编程,那年,我们微机老师向我们推介一门除数学、物理、生物和化学以外的第五大学科竞赛“信息学竞赛”。我当时对计算机的印象,还停留在《热血传奇》《半条命》和《红色警戒》那个阶段,没错,我对计算机的认识都是和游戏相关。当时老师在台上介绍了一大堆东西,现在我已经忘得一干二净了。

但有件事至今我还记得:课间的时候,我问老师:老师,编程学好了,能做游戏么?老师说了一声:恩!就是那种不置可否的“恩”,可以自行脑部相声演员岳云鹏的那个"恩"。对于幼小的我来说,这就是肯定句,从此我就踏上了学习编程的道路。

我所接触的第一门编程语言是 Pascal,这是一门上古级的编程语言,语法风格类似现在的 Python 和 C 的混搭风。现在没有多少人使用的原因,我猜是因为其特立独行的语法规则。Pascal 程序需要你在最开始把所有需要的变量都定义一个遍,然后再描述程序的过程逻辑。所以当时我们打趣说,作为 Pascal 的忠实用户,我们比那些使用 C 语言的要思虑周全。

下面放一段 Pascal 的“判断素数”的程序,你自己感受一下这门上古级语言那遮挡不住的力量。这段代码你看不懂没关系,只是让你对Pascal 有个直观的感受。

program JudgePrime;                // 程序名称
var x, i : integer; f : boolean;   // 变量仅能在此定义
begin
    readln(x);
    f := true;
    if x < 2 then 
    begin 
        write('F'); 
        exit; 
    end;
    for i:=2 to trunc(sqrt(x)) do
        if x mod i=0 then f:=false;
    if f then write('T') else write('F');
end.

在这段用 Pascal 写的判断素数的程序中,第一行是程序的名字,第二行就是定义变量部分,并且变量只能在这里定义。从 begin 到 end. 中间就是我们所谓的程序逻辑部分了,是不是有种看 Python 代码的感觉?

你能想象么,那时稚嫩的我,在定义变量名这件事儿上,把26个英文字母都用遍了以后,最后不得不使用类似 “aa”“bb”“ccd” 这种变量名,往事不堪回首啊。

过了两年,为了参加 ACM 竞赛,不得不学习 C++ 了,准确地说,是学习 C 语言风格的 C++,就像印度人说英语,怎么说怎么一股咖喱味儿。因此,在转 C 语言之前,我还有点儿担心这个过程会比较坎坷,谁知道,就用了几天的时间,就搞定了我编程时候需要的几乎全部语法。你要知道,我学习 Pascal 的时候,可是花了四个月啊!

从这以后,我才意识到那句话的真正含义,那句话是这样说的:“语言从来不是编程的障碍,思维才是”。所谓“思维”大多数的时候,反映出来的是“编程技巧”,更形式化一点,我们叫它 “编程范式” 和 “算法数据结构”,这部分的东西,我后面还会着重讲解,并且会教你一些提升编程技巧方法。

当时的 C 语言,真是一上来就让我欲罢不能。下面我给你来一段判断素数的 C 语言程序,你来感受一下,就像感受一个刚从牢笼中挣脱出来的鸟,正如汪峰有首歌所唱的:这是自由的感觉!

#include <stdio.h>

int main() {
    int x;                   // 定义变量x
    scanf("%d", &x);
    int f = 0;               // 定义变量 f
    for (int i = 2; i * i <= x; i++) { // 定义循环变量i     
        if (x % i) continue;
        f = 1;
        break;
    }
    if (f) printf("F\n");
    else printf("T");
    return 0;
}

和上面的那段 Pascal 程序对比,你发现差别了么?对!就是变量定义这里,C 语言中我想在哪里定义,就在哪里定义!从此我跟 C 语言进入了蜜月期。

C 语言除了可以随处定义变量这个特性,它与Pascal 语言还有什么不同的特性呢?这里就不得不提到我曾经做数独程序的经历了。有一次我在做一个数独的题目,就是每行、每列和每个3*3的宫格内部,不重复地填上1~9这9个数字。

这个问题我曾经用 Pascal 语言做过,解题思路是:每次向函数中传入一个代表数独的数组作为参数,然后不断尝试修改这个数组中相应位置的值,如果尝试进行不下去了,就回到上一个状态,重新尝试。

我就原封不动地将 Pascal 语言的解题思路搬到了 C 语言中,但怎么调试都是错的,我自己反反复复检查逻辑,可就是查不出错误。在挣扎了一下午以后,我终于忍不住,求助了学长,这才发现,有一个关键的语言特性,C 语言和 Pascal 完全不一样。下面我就说说这个事儿,你现在听不懂没关系,希望你记住这个事情,等到我们一起学习了一段时间以后,你再回头来细看这段。

普通变量向函数中传值,就是将原变量中的值拷贝给函数参数的过程,这个过程,我们称作“实参给形参赋值”。原变量就是实参,函数参数就是形参。在这个过程中,本质上还是两个变量,两片独立的存储位置,也就意味着,我们对形参的改动,不会影响实参。这一点上,C 语言和 Pascal 是完全一致的,下面就要说到不太一样的地方了。

请观察图1,在 Pascal 中,如果你将一个数组作为参数传递给一个函数,函数内部还是会复制一份这个数组,也就是说,在 Pascal 中数组的传递过程和普通变量的传递过程没有任何区别。

你再观察图2,图2展示的是 C 语言中数组作为参数传递的方式,你在图中会看到一个 “0x1234” 的信息,我们称之为地址,就类似于你家的门牌号。当我们传递一个数组时,其实在传递的是数组的首地址,也就是说,无论实参还是形参,实际上指向同一片存储区!

总结来说,对于数组,Pascal 函数内外,是两个互不相同,互不影响的存储区。C 语言则是函数内外是同一片存储区,任何一个修改,都意味着外部的数组也被修改了。就是这点差异,导致我用一下午也没找到错误的原因!

看了上面这段,不知道你可不可以理解我当时的困扰。我当时用 Pascal 的语言特性去检查 C 语言的程序,从逻辑上来讲,当然是发现不了任何 Bug 了。当时我还以为这个语言特性,是 C 的特立独行,后来才发现,特立独行的是 Pascal。

从我的这段经历你可以发现,初学编程选择什么语言作为自己的第一门语言是多么重要。如果你选择一个比较“偏”的语言,形成了它独特的语言特性,可能会对你学习其他语言造成不小的困扰。而C 语言,由于它的共通性,很少会出现这样的问题。

最后给你介绍的一门语言就是 Python,上面我们欣赏过了从 Pascal 过渡到 C 语言的神清气爽,那你知道如果你学完 C,再学 Python,是什么感觉么?简直就跟吃了一大口芥末一样,提神!下面来看看 Python 的判断素数的一个程序。

#!/usr/bin/env python
# coding=utf-8
x = int(input())
i = 2
while i * i <= x:
    if x % i == 0:
        print("F")
        break
    i = i + 1
else:
    print("T")

对比上面的两段代码, Python 的这份代码,是不是看着就简洁、清爽?而 Python 为什么被评价为天生就格式优美呢?那是因为,在 Python 中,如果你不按照缩进组织程序的层次关系,你的程序根本没有办法正常运行。

不同的人编写代码可能有不同的风格,就像C 语言,10个人可能就有10种风格,但Python的代码风格就像人的指纹,它是天生的,不管多少人用Python编写代码,可能也只有一种风格。所以无论你是否写过程序,在写 Python 的时候,都将写得很漂亮,很舒服。

最适合学习编程的操作系统是 Linux,Linux 中有一个核心设计思想,叫做“一切皆文件”,理解了文件,就理解了整个 Linux 操作系统,这里说的文件,可不是你所认为的常规的 windows文件。Linux 世界中的文件,就像是我们这个世界中的原子一样,是一种本质。

而 Python 中也有一个类似的核心设计思想,就是“一切皆对象”,理解了什么是对象,你就理解了 Python。而这么抽象的概念,我不认为我现在可以用两三句话就给你讲明白,不过还是那个道理“语言从来不是编程的障碍”,关于对象这个概念,在你日后对编程的知识逐渐丰富起来以后,自然就会明白了。

编程入门,舍我其谁:C 语言

听完了以上三种语言的介绍,你可能已经打定主意准备把界面精练的 Python 作为自己学习编程的入门语言。但是如果看完下面我给你的这张图,你可能需要再考虑考虑。

上图中的时间,只是一个参考,可能你比较有天赋,会比图中所标记时间用时短,可绝大多数的人,只会多于图上的时间,不会更少的。你会看到图上有两条学习路径,绿色的学习路径用时两个月多一点,红色的学习路径用时四个月。其实这张图,我就是想跟你说明,在我们学习过程中的一个重要的概念:学习路径。一个合理的学习路径,可以帮助你大大缩短整体的学习时长,毕竟你的时间才是最大的成本。

其实正如你所看到的,你用相同的时间掌握了 C 语言后,会对你学习其他语言有很大的帮助。反观,如果你一上来掌握的就是拥有很多奇怪语法特性的语言,不仅要花很长时间学,在日后的学习中,你会发现这些语法特性在其他语言甚至都找不到。基于这类知识锻炼出来的编程思维,是不具备延展性的。所以,在选择第一门语言的时候,一定要选择简洁、高效、不拘泥于语法特性的语言。就像学习武功一样,摒弃掉花拳绣腿,去稳扎稳打的进行练习,才是快速成长的诀窍。

这里,请记住:学习编程不等于学习语言,前者包含后者,也就是说想学好编程,不仅是学好语言,还有很多比语言更难的东西等着你呢。

既然要给你讲编程,我决定选择一门既可以带你潜入底层系统一窥究竟,又可以顺畅简洁表达逻辑,还没有特别多奇奇怪怪的语法特性的语言。我希望借助这门语言,让你在学习编程的过程中,能够专注于编程思维训练本身,帮助你一步一个脚印地学习编程,培养编程思维。这门语言,就是我们前文说到的 C 语言。

我有朋友是这样形容 C 语言的,我觉得很贴切,拿过来用一下,他说:学编程就像是学乾坤大挪移,而 C 就是语言中的九阳神功。

推荐书籍

专栏里面呢,由于篇幅有限,没有办法穷尽 C 语言的所有知识。不过,我讲的会是一些容易被你忽视的,容易被你误解的,以及你自学不容易学会的知识点。而关于 C 语言更多的知识,我专门买了市面上最畅销的 15 本C 语言的书籍,经过一番筛选之后,我选出来了以下三本小册子,推荐给你。之所以说是小册子,那是因为他们每一本较其他 C 语言的相关书籍都很薄,内容也很详实准确,并且在内容上,三本有着递进的关系。

课程小结

说了这么多,今天我只是想让你记住一件事情,不同的学习路径,会有不同的时间成本。C 语言只是我们入门编程的一个载体,也是最有效、最深刻的一个载体。从 C 语言入手,会使得你的总时间成本最低。永远记住,扎扎实实,稳扎稳打,才是真正的捷径。

最后呢,请你想一想,在你的学习过程中,有没有哪块知识,是你身边的行业前辈们告诉你很重要的,你一开始忽视了,然后过了好久,才发现,前辈说的对的,欢迎留言给我。

我是胡光,今天我们就先聊到这儿,下期内容,我们不见不散。

我录制了一个关于编程环境说明的视频,如果有对编程一点也不熟悉的朋友,可以看看这个视频,了解一下编程环境。

评论