What & How & Why

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录前一修订版
后一修订版
前一修订版
cs:fundamental:cs61a:week_1 [2023/09/26 01:59] – [Environments] codingharecs:fundamental:cs61a:week_1 [2023/10/27 05:54] (当前版本) – [Conditional statement] codinghare
行 154: 行 154:
 \\  \\ 
 function 的 binding (无论是导入的,还是自定义的),也存储在这里: function 的 binding (无论是导入的,还是自定义的),也存储在这里:
 +\\ \\ 
 +{{ :cs:fundamental:cs61a:global_frame_func.png?500 |}}\\ 
 +我们将存储在 frame 中的 name 称为 **//bound name//**,而出现在函数内部的,同样的 name 称为 **//intrinsic name//**。每个函数可以同时存在多个 //bound name//,但 //intrinsic name// 有且只有一个。
 +==注意事项==
 +  * //bound name// 会在评估期间完成绑定。
 +  * Function Signatures:通过描述参数(//formal parameter//)的数量来区分同名的不同函数。
 +===自定义函数的调用过程===
 +考虑以下函数:
 +<code py>
 +from operator import add, mul
 +def square(x):
 +    return mul(x, x)
 +
 +def sum_squares(x, y):
 +    return add(square(x), square(y))
 +
 +    result = sum_squares(5, 12)
 +</code>
 +  - 首先,python 会评估 name ''sum_squares''. 此时 ''sum_squares'' 与自定函数绑定,其关系存储在 global frame 中。
 +  - 当程序执行到调用 ''sum_square()'',也就是 '' result = sum_squares(5, 12)'' 这一步时,此时:
 +    - 该调用创建了一个 ''sum_suqare()'' 专属的 local frame
 +    - local frame 中保存了 ''sum_suqare()'' 内部  operand 的绑定关系。此处是 ''square()''。需要注意的是这里的 operator ''add()'' 是 built-in(外部导入的),因此其绑定存储在 //global frame// 中,而不是 ''sun_square()'' 的 local frame 中
 +  - 接下来需要应用另外一个自定义函数 ''square()'',因此针对该函数的绑定,需要再创建一个 Local frame 进行存储
 +  - 执行完毕之后,将得到的结果一级一级往上反馈,最终得到最后结果:
 \\  \\ 
-{{ :cs:fundamental:cs61a:global_frame_func.png?400 |}}\\  +{{ :cs:fundamental:cs61a:user_define_func.png?600 |}} 
-我们将存储在 frame 中的 name 称为 **//bound name//**而出现在函数内部的,同样的 name 称为 //intrinsic name//。每个函数可以同时存在多个 //bound name//,但 //intrinsic name// 有且只有一个。 +\\ \\  
-==name 在 evaulation 期间绑定==+可以发现的是: 
 +  * 存储与 //global frame// 中的 name 是可以共享的 
 +  基于 //local frame// 的 name是独立的;比如上例中的 ''x'',即便 name 相同,根据其所在的 local frame, 其绑定的 value 也不同。 
 +==Local names== 
 +函数的实与其 //formal parameter// 无关。比如下面的两个函数实际上是同一个: 
 +\\  
 +<code py> 
 +>>> def square(x): 
 +        return mul(x, x) 
 +>>> def square(y): 
 +        return mul(y, y) 
 +</code> 
 +也就是说,//formal parameter// 的 name 只会在函数内部生效。这样设计的好处: 
 +  * 避免了 //formal parameter// 的 name 与 //global frame// 中的 name 同名带来的问题。 
 +  * 确保了 //formal parameter// 是基于函数独立的;避免了多个函数之间因为参数重名而带来的冲突 
 +我们将 //local name// 生效的区域(函数体)成为它的 //scope//。如果某个 name 无法被访问那么我们就称其 //out of scope//。 
 +==python 命名的选择== 
 +  * lowercase, spearated by underscores 
 +  * 函数名通常表示操作的类型(比如 ''print()''),或是得到的结果(比如 ''max'') 
 +  * argument 使用 lowercase, spearated by underscores,一个词的 name 更好 
 +  * argument 的名字同样也应该表意 
 +  * 不推荐使用单字母参数 
 +===函数与抽象=== 
 +从之前例子可以看到,我们在不考虑 ''square()'' 如何实现的情况下使用 ''sum_squares()'' 函数。我们将 ''square()'' 这种处于另一个函数内部的函数实现**函数抽象**(//functional abstraction//)从这点上来看,下面两个函数是没有区别的: 
 +<code py> 
 +# these two functions are indistinguishable becuase of their return value 
 +>>> def square(x): 
 +        return mul(x, x) 
 +>>> def square(x): 
 +        return mul(x, x-1) + x 
 +</code> 
 +==函数抽象的概念== 
 +函数抽象需要考虑三个核心的属性: 
 +  * The **domain** of a function: 函数可以接受的参数范围 
 +  * The **intent** of function:函数的 input 与 output 之间的关系 
 +  * The **range** of a function: 函数**返回值的取值范围** 
 +==python 的运算符== 
 +  * ''/ /'' 是 floor division, ''/'' 是一般除法,分别对应 ''floordiv()'' 和 ''truediv()'' 
 +===Designing Functions=== 
 +函数设计应该遵循一个 idea: 
 +>//functions are abstractions.// 
 +具体的来说: 
 +  * 每个函数应该只对应一项工作,该工作可以很简单的描述。多个工作应该使用多个函数实现 
 +  * DRY(//do not repeat yourself//)。逻辑只需要被实现一次并反复应用;而不是到处拷贝该逻辑 
 +  *  函数应该被定义为更泛化的形式。比如比起 ''square()'' 函数,我们应该实现的是 ''pow()'' 函数。因为指数运算包含了平方运算。 
 +==Documentation== 
 +python 中通常包括了函数的描述,这类 documentation 被称为 //docstring//: 
 +  * //docstring// 使用首尾三对双引号包含 
 +  * //docstring// 需要遵循缩进 
 +<code py> 
 +>>> def pressure(v, t, n): 
 +        """Compute the pressure in pascals of an ideal gas. 
 + 
 +        Applies the ideal gas law: http://en.wikipedia.org/wiki/Ideal_gas_law 
 + 
 +        v -- volume of gas, in cubic meters 
 +        t -- absolute temperature in degrees kelvin 
 +        n -- particles of gas 
 +        """ 
 +        k = 1.38e-23  # Boltzmann's constant 
 +        return n * k * t / v 
 +</code> 
 +有一类以 ''#'' 起头的信息被称为 //Comments//。 
 +[[https://peps.python.org/pep-0257/|Docstring Conventions]] 
 +====Control statement==== 
 +===statement=== 
 +//statement// 与 //expression// 在本质上有不同: 
 +  * 我们评估(//evaluate//) expression  
 +  * 我们执行(//execute//)statement 
 +//statement// 意味着应用更改:比如赋值,返回等等。//exrepssion// 在被评估的时候也可以被视作 //statement//,但其生成的结果会丢失。比如: 
 +<code py> 
 +>>> def square(x): 
 +        mul(x, x) # Watch out! This call doesn't return a value 
 +</code> 
 +如果希望应用修改(返回返回值),则需要使用 ''return'' statement: 
 +<code py> 
 +>>> def square(x): 
 +        return mul(x, x) 
 +</code> 
 +==Compound Statements== 
 +结构如下: 
 +<code py> 
 +<header>: 
 +    <statement> 
 +    <statement> 
 +    ... 
 +<separating header>: 
 +    <statement> 
 +    <statement> 
 +    ... 
 +... 
 +</code> 
 +<WRAP center round box 100%> 
 +根据上述定义,''def'' 属于 //compound statement//。 
 +</WRAP> 
 +上述的结构可以被视作 //sequence//,也就是该结构总可以分为两部分: 
 +  * 当前的 statement 
 +  * 余下的 statement 
 +这是一种递归的结构。 
 +===Local Assignment=== 
 +用户自定义的函数是对应的 local frame 中运行的。local frame 在该函数被调用时创建;函数体中的 ''return'' statement 会起到重定向的作用。函数什么时候结束取决于 
 +  * 第一个 ''return'' statement 什么时候被执行 
 +  * 返回的值 
 +赋值语句可以处于函数内部。任何函数内部的赋值,其绑定信息都存储于local frame,对外部的 name 不造成任何影响。 
 +===Conditional statement=== 
 +<code py> 
 +if <expression>: 
 +    <suite> 
 +elif <expression>: 
 +    <suite> 
 +else: 
 +    <suite> 
 +</code> 
 +  * 首先会评估 header 
 +  * 某个 header 为 true, 则执行该 Header 下的 suite,其他的 suite 将会被跳过。 
 +===Iteration=== 
 +<code py> 
 +while <expression>: 
 +    <suite> 
 +</code> 
 +===Testing=== 
 +==Assertions== 
 +python 中可以使用 ''assert'' statement 做验证。如果 ''assert'' 返回的对象为 ''True'',那么什么也不会发生。否则,错误会被抛出: 
 +<code py> 
 +>>> def fib_test(): 
 +        assert fib(2) == 1, 'The 2nd Fibonacci number should be 1' 
 +        assert fib(3) == 1, 'The 3rd Fibonacci number should be 1' 
 +        assert fib(50) == 7778742049, 'Error at the 50th Fibonacci number' 
 +</code> 
 +==Doctest== 
 +//docstring// 中可以包含 test case,用于测试: 
 +<code py> 
 +>>> def sum_naturals(n): 
 +        """Return the sum of the first n natural numbers.
  
 +        >>> sum_naturals(10)
 +        55
 +        >>> sum_naturals(100)
 +        5050
 +        """
 +        total, k = 0, 1
 +        while k <= n:
 +            total, k = total + k, k + 1
 +        return total
 +        
 +>>> from doctest import testmod
 +>>> testmod()
 +TestResults(failed=0, attempted=2)
 +</code>