MIT《计算机科学与编程导论》课堂笔记
Introduction to Computer Science and Programming
Lecture 1
Focus on concepts and tools of computational thinking.
Learn how to think like a computer scientist.
Skills:
computational thinking
understand code
understand ability & limits
map it into computation
课程核心内容是计算思维的概念和工具,学习如何像一个计算机科学家一样思考。
掌握的技巧:学会计算思维,能读别人的源码(非常有用),了解计算的局限性和代价,用计算来描述其他领域的问题。
Not be handing out class notes. Students learn best when they take notes even if they never look at them. The process of writing is exercising both halves of your brain and
本课程不分发课堂笔记。最好的学习方式是记笔记,尽管很多人过后从来都不看。
但记笔记的过程能够同时锻炼左右脑。
Think like a computer scientist
-What is computation? Separate out which is computer from computational thinking.
-What is knowledge? Two categories:
Declarative: √x is y means y²=x, x≥0
Imperative:
start with guess G
if G²≈x stop -> G
otherwise new guess G <- (G + X/G) / 2
repeat
要区分开计算机和计算思维。
陈述性知识和过程性知识。
过程性知识是关于如何推导的过程。一个古老的例子计算根号2,据说是古希腊数学家希罗Heron的贡献。
How do I build a mechanical process to capture that set of computation? A simple way is to build a little circuit to do this. That is actually an example of the earliest computers.
Fixed-program computers
-calculator
-Atanasoff, 1941 -> solved linear equations
-Alan Turing bombe -> used during WWII to break German Enigma codes
如何建立机械工艺来实现这种计算。简单的方法是建立一个小电路来实现,有存储的部件,加减法部件以及判断的部件。这其实就是最早的计算机,称作“固定程序计算机”。
例如只能做计算的计算器,解线性方程组的阿塔那索夫,二战中用于德国恩格玛密码的图灵。
Suppose you could build a circuit with the following property: the input to this circuit would be any other circuit diagram. That circuit would wonderfully reconfigure itself to act like the circuits diagram.
It's going to change how it does the computation. That would be cool and that exists. It's called an interpreter. It is the basic heart of every computer. What it is doing, is saying, change the game. This is now an example of a stored-program computer.
假设一种电路可以接收任意其他电路图作为输入,对它输入某特定用途的电路图,该电路就会奇妙地重组自身,让自己按照输入电路图进行工作。可以想象很多微小的机器人四处运动,插拔电线和元件进行重组,这有点挑战性。
它能够改变自己进行计算的方式。这确实存在,称作“解释器”。它是所有电脑的基础和核心,其作用是改变规则。这就是所谓的“存储程序计算机”。
A traditional recipe actually is based on a small set of primitives, a good chef with that set of primitives, can create an unbounded number of great dishes. Same thing holds true in programming. Given a fixed set of primitives, a good programmer can program
anything.
In 1936, Alan Turing showed that with six simple primitives, anything that could be described in a mechanical process. That's an incredible statement. It says, with six primitives, I can rule the world. I can program anything.
A couple of really interesting consequences of that. One of them is anything you can do in one programming language, you can do in another programming language. There is nothing that you can do innn C that you can't do in Fortran. It's called Turing compatibility.
传统的食谱都有一系列基本指令,一个好的厨子能够自由组合它们创造出无数美味的菜肴。编程也是如此。只要有适用的基本指令集,好的程序员能编出任何程序。1936年,图灵认为只要六条简单的基本指令,一切都可以通过机械工艺解决。随之产生了一些有趣的推论。其中有条是,只要能在一种编程语言中实现,就能在另一种语言中实现。C中能实现的没有Fortran不能实现的。这称作“图灵兼容”。
Describe recipes -> need language
Python
-High vs. low level
-General vs. targetted
-Interpreted vs. compiled
语言的分类方法:高级与低级,通用与特殊用途,解释型与编译型。Python属于高级、通用、解释型语言。
Syntax - what are legal expression
"cat dog boy" (Lots of help. Python comes built-in with syntax checker, find one error each time)
Static semantics - what programs are meaningful, which expression make sense
"My desk is Susan" (Some help.)
Full semantics - what does program mean, what happen when run
语法用来描述语言中,什么表述是合法的。静态语义表示什么程序是有意义的,哪种表达是有意义的。
完整语义即程序想达到什么目的,运行程序会产生什么效果。这就是我们编写代码的意义所在。
In Python, we have two primitive data elements to start with.
Numbers:
3 - integer
3.14 - floating point
Strings:
'abc'
And assiocate these simple things with operations: +,-,*,/
>>> 52
52
>>> 'abc'
'abc'
>>> 52*7
364
>>> print 52*7
364
>>> print '52*7'
52*7
>>> print '3'*'3'
TypeError
>>> print '3'*3
333
>>> print 52a
SyntaxError
>>> 3**5
243
>>> 3/5
0
>>> 3.0/5
0.5999999...
>>> print 'abc'+ 'de'
abcde
>>> myString = 'eric'
>>> myString
'eric'
两个字符串相乘会产生错误,但是一个字符串与数字相乘会被解释为将字符串重复几次。好的设计还是坏的设计?
Lecture 2
Primitive data - numbers & strings
Combine in expressions: operands & operators
Interpreter - evaluates & prints
Script - no print unless explict
在脚本中执行时,如果没有显示的打印的话,每个表达式执行后是不会打印返回值的。
这一点与在控制台直接输入不同。
Type convertor: str(3)+'ab'
The syntax is OK but it is a static semantic error. Because the operator was expecting a particular kind of structure there.
Type checking
-weak vs. strong typing
Type discipline
>>> 'a' < 3
False
>>> 4 < '3'
True
>>> '4' < '3'
False
语法没有问题,但这是一个静态语义错误。因为运算符要求一种特定的结构。?
字符与数字按照ASCII码比较。
Variable Assignment
x = 3*5
y = 15
z = x
When I create a binding, I'm taking a variable name, in this case x, stored somewhere in a table, and I'm creating a link or a pointer from that name to that value.
This is a nuance. It's going to make a lot more sense later on, when we introduce mutation into our language. Don't think of it as a specific box into which we're putting things, think of it as a link to a value.
赋值就相当于取某个变量名,这里是x,存在某处,然后创建一个变量名与值之间的连接或指针。
注意这里的差别。这种思想在以后非常有用,特别是引入“可变性”的概念后。千万不要将赋值考虑为往盒子里装东西。
Type of variable - It inherits it from its value.
Dynamic types
x = 3
x = 'abc'
It changes depending on what the current value is. Or said a different way, if somewhere later on in the program I do this, x now has changed its type from INT to string.
Don't change types arbitrarily.
变量的类型是什么?答案是:变量的类型继承于其值。
变量的类型随着当前值的类型而改变。换言之,如果之后程序里出现这样的语句,x就会从整型变为字符串型。
不要随意改变变量类型。
Statements = legal commands that Python can interpret.
-print, assignment
Variable names - important to document, keywords excluded
A branching program is that can change the order of instructions based on some test. And that test is usually a value of a variable.
if (x/2)*2 == x:
print 'Even'
else: print 'Odd'
Syntax: colon is important, it identifies a block of instructions. The colon is start, and the carriage return is the end.
分支程序是执行过程中,可以根据判别结果改变指令顺序。而判别通常是对某变量值进行的。
冒号表示开始,回车表示结束。
Lecture 3
DATA OPERATIONS COMMANDS
number +,* assignment
string input/output
boolean and,or branch
loop mechanisms(while)
Good programming style
-comment
-type displine
-good variable names
-test all possible logical path
Iterative programs
-choose variable that "count"
-initialize outside the loop
-setup end test(variable)
-construct block
-change variable
-what to do when I'm done
温习前两节课的主要内容。首先是编程的基本元素:数据、操作、命令语句。以后将要用这些基本元素组成有用的程序。
另外是好的代码格式,如注释、类型规范、起好的变量名、测试所有分支语句。最后是编写循环语句时要思考的一些问题。
Useful tool
-Flow chart -> design code, complexty of calculation
Code:
x = 16
ans = 0
while ans*ans < x:
ans = ans + 1
print ans
-Simulate the code
ans x ans*ans
0 16 0
1 1
2 4
3 9
4 16
5 25
print ans=5 (find bug)
两样有用的编程工具:流程图和代码模拟。
早期的程序员工作时都会拿着中间带各种流程图图案的尺子。流程图帮助我们理清逻辑,并可以从流程图清晰地看出代码的复杂度。如左边的流程图是与输入规模N成线性关系的,而右边的计算奇偶数的流程图是常数的。代码模拟帮助找出常见的Bug,如off-by-one,以及>=边界条件。
Defensive programming
First question is, for what values of integers does this code terminate?
And second question is, for what values of x does it give me back the right answer?
Those are two things that you'd like to do with every looping construct you write.
You'd like to be able to assure yourself that they will always terminate, and assure yourself that it does give you back a reasonable answer.
第一个问题是,什么样的整数x能让程序停止;第二个问题是,什么样的x能得出正确的答案?
以上两点是对于任何循环都必须检测的。你必须保证程序能够终止,并且保证结果是合理的。
上面的代码对于x=-16以及15结果是0和4,都不正确。看以下改进代码:
ans = 0
if x >= 0:
while ans*ans < x
ans = ans + 1
if ans*ans != x
print x, ' is not a perfect square'
else: print ans
else: print x, ' is a negative number'
The defination of defensive programming is to make sure that I'm going through all possible paths through the code, make sure I'm printing out or returning useful information for each path through the code, make sure that for all possible inputs there is a
path through the code or a way to get through the code that does not cause an error or infinite loop.
The basic idea of defensive programming is, to assume that A. if you're getting inputs from a user, they won't necessarily give you the input you've asked for. And B. if you're using a piece of a program written by a programmer who is not perfect, perhaps yourself,
there could be mistakes in that program, and so you write your program under the assumption that, not only might the user make a mistake, other parts of your program might make a mistake.
防御式编程是指:保证遍历了代码中的所有分支;保证对每个分支,打印或返回的结果有意义;保证所有可能的输入数据都能对应一个分支,且不会产生错误或无限循环。防御式编程的基本假设是:一、对于程序的使用者,他们可能不会安装要求输入数据;二、对于程序本身,你使用的一段代码,编写者(也许是你)也不是完美的,可能会有一些错误,因此写程序时必须做好最坏假设。不仅使用者会犯错,程序自身的其他部分也会犯错。
For loop
for <var> in <some data collection>
block of code
可以不用关心计数器了,循环会自动增加计数器并停止循环。
Tuple - ordered sequence of elements (immutable)
foo = (1, 2, 3, 4)
Selection: foo[0], foo[-1]
Slicing: foo[1:3], foo[:3], foo[2:]
Concatenate: foo[1:3] + foo[2]
x = 100
divisors = ()
for i in range(1, x):
if x%i == 0:
divisors = divisors + (i,) -> badly overload +
strings - also support selection, slicing
元组是有序的元素集合,之后将会学到它与数组的区别(注意圆括号与方括号)。
String也是有序的数据集合,也支持元组的选择、切片、拼接操作。
The last thing I want to point out is, I started out with this list. I haven't added anything to the list. I've added the ability to have more complex data structure here. But I dropped a hint in the first lecture about what you could compute with things. In
fact if you think for a second about that list, you could ask what can I compute with just that set of constructs? And the anser is basically anything. This is an example of what is referred to frequently as being a Turing complete language. That is to say
with just those set of constructs, anything you describe algorithmically you compute.
最后一点:这节课开始时,我就列出了这些命令。到目前为止,我没向这个列表中加入任何东西。今天只是讲了一些更复杂的数据结构。第一讲我就暗示过,关于计算我们能做些什么。就这个列表,各位想一下,用这些构造我能进行什么计算?答案是:几乎任何计算。这就是“图灵完备语言”的典型例子。也就是说只需要这么几个构造,我们可以用算法来描述或计算任何东西。