按 ‘ infotech ’ 分类归档

程序员入伙书——数组

从前我用过的程序示例里面,使用的数据都是分离的、独立的。就是说,每个常量和变量,各有各的用途,没有“我们是一伙的”这样的共性。而在现实世界里,成群结队地出现的数据是大多数。例如一架飞机上的旅客,一个班级的学生,奥运会的参赛国。

我见过的所有编程语言都有数组(Array,List)的概念,用来存储具有共同属性的批量数据。在Python里,数组用一对中括号包围的一群数据来表示,数据之间用逗号隔开。例如:

>>> countries = ['China', 'USA', 'Germany', 'Canada', 'Japan']
>>> countries[0]
'China'
>>> countries[3]
'Canada'
>>> countries[4]
'Japan'
>>> 
💡 Python语言允许数组的每个元素类型不同,例如字符串和数字可以混装在同一个数组里面,如[3.14, ‘China’]就是一个正常的Python数组。其它的编程语言通常没有这么大度。在实际应用中,即使是Python,也很少遇到各元素类型不同的案例。

在这里,countries就是一个数组,它有五个元素(Element),每个元素都是一个字符串表示的国名。引用这些元素的时候,我们使用一个整数,来指明它在数组里的位置,这个整数叫做下标(Index)。和大家的习惯稍有差异的是,在Python里,当我们说“数组的第一个元素”时,我们用的下标是0,第二个元素的下标是1,第三个元素的下标是2,依此类推。所以上面的程序范例里面,countries[0]打印出来的是第一个国名China,第五个国名使用的下标是4。

如果你觉得这个规则挺怪异的,想一想这个问题:进入一座楼房,爬到一楼需要爬几层楼梯?爬到五楼需要爬几层楼梯?然后你就释然了,因为对于计算机来说,数组就像楼层,一楼就是它的第一个元素,其它的楼层都是相对于一楼的偏移量。英式英语里,一楼叫做Ground Floor,二楼叫做First Floor,三楼叫做Second Floor,这就是计算机式的思维。

C/C++、Python、Perl、Java、PHP等语言里,第一个元素都是用下标0来表示。FORTRAN、BASIC、PASCAL等语言里,第一个元素是用下标1来表示。这两种表示方法都是合理的,照顾不同人群的思维习惯。

在刚才这个数组里,使用countries[5]会看到什么?
猛击阅读全文

程序员入伙书——轻松一下

一口气写了十几章,轻松一下。

读者老爷可能有点昏沉了,说,这么多章节了,怎么还没见到一个Windows程序呢?咱们就写一个。

在Python Shell里按Ctrl-N(或者点File菜单,选New Window),在弹出的编辑窗口里输入——或者拷贝-粘贴也行,反正今天是“轻松一下”:

import tkinter
root = tkinter.Tk()
root.geometry('240x180')
root.title('hello, world')
tkinter.Label(root, text = "hello, world").pack(fill="both", expand=1)
root.mainloop()

hello-world-tk

按Ctrl-S(或者点File菜单,选Save),随意起个名字,存好文件之后按F5。

怎么样,看到Windows版的hello, world了吧?虽然看起来十分傻,但既然能写hello, world,就能写别的,就能放按钮,就能画图表,就能放菜单,能写一切能想出来的东西。要紧的不在于界面是什么,而在于理解漂亮的外表背后发生的事情。况且,就连这个漂亮的外表本身,也是由灰头土脸的程序写成的。

程序员入伙书——两种循环

其实咱们早已看到过循环了,只是当时并没有要求大家看明白。

循环,就是让程序反复执行同一段代码。利用计算机速度快,脾气好,不怕麻烦的优势(这几点,人类确实比不上),也省得人们把相似的任务反复书写几百遍。

有两种循环,一种是事先预知要循环多少次的,一种事先不知道。这两种循环,咱们在《程序在干什么?》一章里见过。那里头讲过三个例子,第一个例子是从1加到100,我们都知道要循环100次。第二个例子是九九乘法表,我们知道要重复做9行,每行做N列(N等于当前的行数)。第三个例子是猜平方根,这个就不能实现确实要循环多少次了,只隐约觉得精度好过亿分之一就可以结束。

预知循环次数、知道每次循环时要喂什么数据给程序的,叫做for循环。for的意思是:对于(for)每次循环,使用这些值……

预先不知道循环次数,只知道循环的结束条件的,叫做while循环,意思是:当(while)某条件判断为真的情况下,反复执行这段程序……

其实for循环是while的一个特例,例如那个从1加到100的,下面的两段程序,一个用for,一个用while,得到的结果是一样的:

>>> total = 0
>>> for i in range(1, 101):
	total += i
	
>>> total
5050


>>> total = 0
>>> i = 1
>>> while i < = 100:
	total += i
	i += 1
	
>>> total
5050
>>> 

所以我们先讲while。在Python里,while的用法是:

while 条件判断表达式:
	做这个
	做那个
	东张西望

凹进去的这段程序,只要“条件判断表达式”为真(True),就会被反复执行。上面的累加例子里,只要i小于等于100,累加就会持续下去。

while太简单太直观了,没啥可说的,直接说for吧,for的用法是:
猛击阅读全文

程序员入伙书——并且、或者、不是、真真假假

上一章介绍if-else时,我在结尾举了个程序优化的例子,里面用到了一个词:and,我答应会细细讲到它,现在这个章节就是。

if-else的执行过程是这样的:比如语句是if a > 3,那么它就会看,a大于3吗?如果a的当前值等于4,那么4 > 3这个表达式就会算出一个“真”值,if a > 3就成了if True。程序看到了if True,就决定执行这段程序。如果a等于2,它就会最终看到if False(“假”值),if False的结果就是使程序跳过这段代码,转而执行下一个判断。

做个实验,这个实验证明if True所对应的程序段会无条件执行,而if False所对应的,则铁定不执行:

>>> if True:
	print("I am in the True block")

	
I am in the True block
>>> if False:
	print("I am in the False block")

	
>>> 

True的意思是“真”,False的意思是“假”。数学上说的真命题、假命题,和这两个概念等价。

不是所有的条件判断都是a > 3这么简单的,有时需要把几个条件组合起来。例如“晚上如果停电,我就点蜡烛”这句话,等同于“如果到了晚上,并且停电,我就点蜡烛。”只有“晚上”和“停电”同时满足时,才会引起点蜡烛的动作。而如果是大白天、或者有电、或者既是大白天又有电,那就没必要点蜡烛了。“他夫妻俩都去,我就去”的意思是“他去,并且她去,我就去。”如果这小两口有一位不出席,我就未必去。

这个表达“两个条件同时满足,才能得到True”的词:“并且”,在Python里被写为and。and两边的值必须同时为True,才能得到True的结果,否则结果是False。

>>> True and True
True
>>> True and False
False
>>> False and True
False
>>> False and False
False
>>>

and对条件要求相当苛刻,相当于给了所有投票者一票否决权。另有一种要求不那么苛刻的,只要有一个投了赞成票,就绿灯放行。这种运算叫做“或者”,Python里写作or。

“两个人有一个好的,这架就打不起来。”这话的逻辑就是or。意思是说,如果张麻子是好的or王二狗是好的,甚至两个人都是好的,就不会打得头破血流。

1971年,副统帅说想动一动,周丞相说,如果想动一动,需要周、黄、吴、李四人同时下令。但李是副统帅家的人,把这话解释为,只要周黄吴李中有人下令,就可以动。于是山海关的那架三叉戟就动了,动静还挺大。

这个例子,就是本来应该用and的地方写了or,原本是“周同意 and 黄同意 and 吴同意 and 李同意”变成了“周同意 or 黄同意 or 吴同意 or 李同意”。把and和or用错,很多老程序员都可能会犯。众所周知,那架三叉戟后来掉下来了。这个故事告诉我们:and和or千万不能弄反,故意制造Bug就更要不得了。
猛击阅读全文

测试一下用Email发博客

hello, world

hello, blog via email

人闲桂花落
夜静春山空
月出惊山鸟
时鸣春涧中

程序员入伙书——如果、那么、否则

在《初涉算法》一章,我说过程序没什么神秘的,无非是做三件事:

  1. 计算
  2. 流程控制
  3. 输入输出

关于基本的计算,前头几章已经讲过了。现在开始说说流程控制(Flow Control)。

流程控制是程序的核心价值。如果一段程序从头到尾平铺直叙,一条大路走到黑,就不好意思说自己是程序了,顶多是在打算盘。试想,如果没有流程控制,“从1累加到100”的任务就只能这样完成,老没劲了:

total = 0
total += 1
total += 2
total += 3
:
:
total += 99
total += 100

一个有趣的程序,一定有种种的岔路和转圈。读者可能会问:你不是说计算机不会思考,所有的行为都是已知的么?岔路的变数从何而来?俺回答:道路是已知的,走路的人是未知的。种种变数,来自于人类或其它机器的输入。例如你正在看这个页面,计算机不知道你下一步是往上翻屏、往下翻,还是关掉窗口。你的下一步动作是机器无法预知的,但计算机做好了应对你的任何操作的准备——只要你不掐电源。

下面我就开始介绍“岔路”了:如果这样,如果那样。

举个例子,Python有个功能叫max,如果你在Python Shell里写max(1, 3)按回车,max一定会回答“3”。我们看看,如果自己来写这个功能,程序是什么样的。尝试一下这个例子——在Python Shell下按Ctrl-N打开一个窗口,输入这段代码:
猛击阅读全文

程序员入伙书——赋值语句

赋值语句,就是把一个值搁到一个变量里的过程。

完成这个过程很简单,一个“=”足矣。打开Python,随意输入类似这样的语句:

>>> radius = 10
>>> pi = 3.1415926535897932
>>> circumference = radius * pi * 2
>>> circumference
62.83185307179586
>>> 

看看,几条赋值语句就把半径等于10的圆的周长求出来了,是不是很容易?这些语句当然可以简单地写成10 * 3.1415926535897932 * 2,这里演示的是思考的过程。

初学编程的人,容易在两件事上不习惯,我这里单列出来:

一、“=”不是“相等”的意思,而是“把等号右边的值算出来,放到等号左边的变量里去”。
二、赋值完成后,等号左边变量原有的值就被覆盖了,消失了,找不回来了。

这些要点,以前举例时说过或者暗示过,这次隆重重申一下。为了体会第一点,试试这段代码(以前的例子里挺常见的):

>>> total = 0
>>> total = total + 3
>>> total
3
>>> total = total + 4
>>> total
7
>>> 

像total = total + 4这种代码,现在在我眼里,是再正常不过的东东,但刚学编程的时代,是每次一瞧心里都咯噔一下的,因为把“等式”两边各减去total,即得到0 = 4。这怎么可能?!现在看顺眼了,因为我现在明白这句话就是三个步骤:把total的值读出来放到一个运算器里,在这个运算器里加4,把运算器的结果放回到total里去。
猛击阅读全文

面试拒人连载

今天这位提前十几分钟就来了,坐着大厅的沙发上等钟点。我知道他来了,因为他的简历上有个人照片,而且,从我的座位开始,眼光往左随意一瞥,穿越几个同事,闪过屏风微妙的弧形,透出落地窗玻璃,再挤进几株绿色植物之间的缝隙,正好就能看到他坐的位置。我就这么看了他一会儿,他就那么闲坐着,一会儿仰头舒服地靠在沙发上,一会儿低头看手机。如果他知道三十米外有这么一双眼睛默默地看着他,一定会脊背发凉的。

57分的时候,他消失了。我座位上的电话也应声响起,人事打来的。我接了,问:“人来了?”那边说:“没有,说再过三分钟到。”哦……如果不是我认错了人,那就一定是忽然想起尿急,所以才有三分钟这么精准的估计……

以上是花絮,虽然不造成拒人的直接理由,却留下了计划不周的印象。

拒人的直接原因如下:

首先是英语差(蹦字型),其次是当被问到标准问题“如何理解我们公司,为何认为自己可以胜任”时,回答“不知道公司做什么,只知道很有名,自己想work better(不明白这句话的含义,但我已经了解他的英语水平,就没追问)”。

致命伤:请他介绍自己做过的项目,他先从“编码数据是这样分发的”开始,我满耳朵听到了“这样的架构可以更通用地处理编码数据”之类的虚词,只好坦诚告诉他,我没听懂,请他说说被处理的编码数据是从哪来的,干什么用的。他说,数据是一些参数。我问是什么参数?答是二进制或十六进制的。问是二进制或十六进制的什么?答是这样的信息。问是什么信息?答是数据包……

我不得不说,抱歉啊,我刚才听到的是:编码数据是参数,参数是数字,数字是信息,信息是数据包。可我还是不知道数据在物理上代表什么,它从哪里获得的。

最后反反复复问了很久,总算问出点眉目了。就是个卫星或飞船的地面监控站,接收到卫星/飞船上的种种传感器的测量数据,由于这些数据从卫星上发来时是通过同一个天线,所以收到之后需要甄别一下:这份数据是卫星姿态的,那份数据是电池容量的……这些甄别和后续的解码过程,过去是在一个大程序里完成,现在他把程序修理了一下,变得更加模块化了。

趁他转过身去在白板上画框图的时候,我和人事的小妹妹对望一眼,摇摇头。她凑过来悄悄地说:可以早结束。

于是就这样了。

程序员入伙书——运算符和表达式

上次我们说到了简单数据,现在就要说说怎么烹饪这些数据。因为我们的示例语言是Python,所以这里的内容只能保证对Python完全正确,其它的语言会有相似内容,但不一定完全相同。不用担心,各种编程语言的要素其实是差不多的,学会一门语言的思路,其它的也能很快融会贯通。

适用于数字的运算符

+       -       *       **      /       //      %
< <      >>      &       |       ^       ~
<>       < =      >=      ==      !=

其中,第一行运算符是一般的数学运算,第二行是计算机特色的位运算(不用担心看不懂,后头会讲到),第三行是数值比较。

先说第一行:+和-都很直观,加减是也。*是乘法,因为计算机键盘上没有×号,所以拿星号表示。你会问为什么不用x来代替?我回答:因为x是一个英文字母,会被Python当作一个变量名。

**是乘方的意思,例如2**3等于8,2**-1等于0.5,2**0.5等于1.4142135623730951(即根号2)。你可能会如梦方醒地说,既然可以这么方便地求解一个数的平方根,为啥当初要写那么复杂的一个算法?俺一边躲闪着观众的飞石,一边说:一、那段程序的主要目的,是演示二分查找的算法。二、其实在计算机的芯片级指令里也并没有乘方功能,**运算符的简洁外表的背后,隐藏着泰勒级数展开的算法,不比我那段程序轻松。

/是除法。小时候如果在试卷上这么写,是会被扣分的。老师只允许用÷号,或者以分数形式表示。键盘上没有÷号,就只能用/了。

//是除法取整数商。注意它和/是不同的:3/2的结果是1.5,而3//2会把那个小数部分.5扣除,只留下1。同理,1//2等于0,而不是0.5。拿这个运算符算整数是安全的,而算实数要小心,例如你可以试试0.7//0.1,和《两个世界》一章里的计算结果是一样一样的:6,具体原因在那一章已经讲过了。

%是除法求余数,例如7%4等于3,3%2等于1,8%4等于0。这个运算符很实用,常用来判断一个数字能否被另一个数整除(余数为0),或者把随机数字限定在一个范围内(不需要十分懂下面这个例子):

>>> import random
>>> random.random()
0.08662194959873759
>>> int(random.random() * 1E10) % 10
9
>>> int(random.random() * 1E10) % 10
5
>>> int(random.random() * 1E10) % 10
0
>>> int(random.random() * 1E10) % 10
4
>>> int(random.random() * 1E10) % 10
6
>>> int(random.random() * 1E10) % 10
2
>>> int(random.random() * 1E10) % 10
1
>>> int(random.random() * 1E10) % 10
8
>>> 

 

暂且跳过第二行的位运算,这些概念目前有点难,大部分情况下也用不着,先介绍第三行的比较运算吧。

<>       < =      >=      ==      !=

这六个运算符十分好理解,从左往右,分别是小于、大于、小于等于、大于等于、等于、不等于的意思。怎么样?很简单吧?计算机键盘上没有≤、≥、≠这些字符,所以用< =、>=、!=来代替,就和刚才说过的乘除法似的,比较好懂。对于==,就得多解释一下:为什么不用单个的=呢?因为单个的=被用来表示“赋值”的含义了。看下面和Python Shell的交流过程,来加深理解:
猛击阅读全文

程序员入伙书——简单数据

一边写一边问读者对这个系列教材的感觉。组里的小姑娘说,读下来,照着做,目前理解上没问题。不过她也说,她的室友是学新闻的,对编程不感兴趣,读这些文章时,反映“读不太懂”。她又说,直到现在还上开胃菜,进度有点慢,希望尽快吃到正餐。

好,正餐来了。

在《程序在干什么?》一章,我说过程序最基本的功能是计算——如果没有计算,我们也不需要计算机了。计算机的运算和人类的数学不同。人类的数学可以设置未知数,然后用推导方法把这个未知数的实际值逼出来。计算机的每一步运算,参与者都是已知的,只是有些参与运算的值永远不变,有些一会儿装三个苹果,一会儿装一百个大象。永远不变的被称为“常量”,可能改变的被称为“变量”。

以下我要说到的规则,仅保证对Python语言有效,其他语言虽然会很相似,但可能有自己的特色法则。

常量

有基本的两种常量:数字和字符串。快捷地描述它们的区别:数字是数学,字符串是语文。

数字很直接,0、-3.5、2012、378.2452,这些数学上看着顺眼的,都是Python语言里合法的数字。另外有些不太顺眼的数字,例如.3和5.,Python也宽宏大量地认为它们是合法的数字,.3意思就是0.3,而5.意思就是5.0。有些巨大的数字或者极小的数字,用科学计数法表达更为便捷的,如1.5×109或者3.0×10-32,Python不会让你辛辛苦苦埋头去数0,而是可以分别表示为1.5E9和3.0E-32,这里的E可以小写。指数前也可以加正负号。
猛击阅读全文