What & How & Why

这是本文档旧的修订版!


Welcome

week 1 notes


Python 基础

基本操作
  • <Control>-P (previous)
  • <Control>-N (next)
  • <Control>-D exits a session
导入

# import lib from specific url
from urllib.request import urlopen

Elements of Programming

编程语言通常会提供三种方式来生成复杂的指令:

  • primitive expressions and statements: 编程语言提供的最基础的单位
  • means of combination: 由最简单的单位组合而成的复合指令
  • means of astraction:复合指令可以被抽象,也就是将复合指令命名,并整合为一个单元来使用。
编程中的两种元素
  • data:需要被操作的内容
  • function:制定如何操作 data 的规则

expressions

表达式包括两种:

  • primitive expressions:比如数字,通常以算数表达式的组合形式出现(operand + operator(infix) + operand)
  • call expressions
call expressions

call exressions 是复合表达式的一种,是 $f(x)$ 的一般形式,指计算(mapping)的过程。其特点:

  • 组成:函数的名字,括号,参数(argument
  • 参数可以有多个,如果有输出,输出也可以有多个。
  • 可嵌套

call expressions 通常是如下的形式:

  • operator 指定了 function
  • 执行的过程是 function 以指定的 argument 调用,并返回了value
  • argument 的顺序 matters
  • 可以使用 function name 取代数学符号(infix notation)进行函数调用,没有歧义并调用方便

name and the environment

name 是一种用于关联 computational objects 的方式。name 可以通过与该对象绑定(binding)的方式来与其关联,从而达到通过 name 来操作该对象的目的。name 的绑定方式有 3 种:

  • import:从外部导入已绑定的 name,使其对本地可见
  • assignment:最简单的抽象方法;使用自定义的 name 和赋值操作,将 name 与对象关联
  • define a function:以函数的方式将自定义的 name 与对象关联,对象通常是复合对象。

由于这种绑定关系的的存在,计算机需要分配一部分空间存储这些 name, object 以及关系。被分配的空间被称为环境environment)。下面是 python 的示例:

#binding example
#import name with bound function

>>>from math import sqrt
>>>sqrt(256)

#assign name to a value
>>> radius = 10

#assgin name to an function
>>> max
#<built-in function max>
>>>f = max

#define a function
def square(x):
    return x*x

name in python
  • built-in function 的 name 可以直接与其他对象绑定
  • 每次修改 name 对应的数据时,都需要进行独立赋值。只修改一个 name 并不会影响其他 name 绑定的值:

>>> area, circumference = pi * radius * radius, 2 * pi * radius
>>> area
314.1592653589793
>>> radius = 11
>>> area
314.1592653589793
#another assignment needed for updating the value of area
>>> area = pi * radius * radius
380.132711084365

  • 赋值操作符右边的所有表达式都会在与 name 绑定之前先被衡量,比如下面的交换,x,y 先被取值,再进行了重新绑定:

>>> x, y = 3, 4.5
>>> y, x = x, y
>>> x
4.5
>>> y
3

表达式解释的顺序

基础的解释顺序为:

  1. operator
  2. operand(可能是 primtive expr,也可能是 subexpression)
  3. subexpression 的 function (operator)

特点是:要计算主表达式,就得先计算出其子表达式的值。这种评估的过程被称为递归recursive

Nested Expressions

如果 operand 是子表达式(sub-expression),那么对该表达式按基础顺序继续执行,直到得到值(value)。例子如下:


该结构被称为表达式树expression tree

注意 CS 中树的构建是从上往下的,也就意味着求出根表达式需要求出其节点的值。而相比一半的算数式子,表达式的结构非常有利于解释执行(解释)的顺序,尤其是在多层嵌套的前提下。

注意事项
  • environment 提供 name 的意义,以及评估 name 的环境。无意义的 name 参与的运算也是意义的
  • statement 不会被评估,但会被执行(exxcuted);也就是说,statement 修改 name 的内容,但不会输出新的内容。比如:

# the operation does not return any values, but changes value of x
x = 3

纯/非纯函数

纯函数pure function)指有输入(argument)并会产生输出 (the result) 的函数,比如:

>>> abs(-2)
2

  • 纯函数只影响其输出结果(返回值)
  • 相同参数的纯函数返回结果也相同

非纯函数Non-pure functions)指应用时带有“副作用(side effects)” 的函数(通常指改变解释器状态)。打印是一种常见的副作用:

>>> print(1, 2, 3)
1 2 3
实际上,print() 函数的打印值并不是其返回值。其返回值是 NoneType 类型的,值为 None 的变量:
>>> print(print(1), print(2))
1
2
None None

纯函数的优点
  • 在复合的情况下,纯函数会更可靠
  • 纯函数可以更有效的被用于函数的嵌套中
  • 纯函数测试起来简单(同样的 argument 永远都会返回对应的结果)

Defining New Functions

自定义函数是抽象过程中更加高级的做法。这种做法允许我们将 name 绑定到指定的复合指令集合上。比如这段程序:

>>> def square(x):
        return mul(x, x)
实际上使用了 square 来抽象了让 x 自己乘以自己的过程。此处的 x 被称为 formal parameterx 为自乘的数据提供了 name。

How to define a function

def <name>(<formal parameters>):
    return <return expression>

Environments

environments 通过一系列的 frame 来衡量表达式。以 frame 为单位,每个 frame 内部都会存储对应的 name,以及该 name 绑定的 value.

在 python 中,默认存在一个全局(golbal)frame。赋值 / 外部导入的 name 都会存储在这个 frame 中:


function 的 binding (无论是导入的,还是自定义的),也存储在这里:

我们将存储在 frame 中的 name 称为 bound name,而出现在函数内部的,同样的 name 称为 intrinsic name。每个函数可以同时存在多个 bound name,但 intrinsic name 有且只有一个。

name 在 evaulation 期间绑定