What & How & Why

差别

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

到此差别页面的链接

两侧同时换到之前的修订记录前一修订版
后一修订版
前一修订版
cs:fundamental:cs61a:week_1 [2023/09/26 02:22] – [自定义函数的调用过程] codingharecs:fundamental:cs61a:week_1 [2023/10/27 05:54] (当前版本) – [Conditional statement] codinghare
行 178: 行 178:
   - 接下来需要应用另外一个自定义函数 ''square()'',因此针对该函数的绑定,需要再创建一个 Local frame 进行存储   - 接下来需要应用另外一个自定义函数 ''square()'',因此针对该函数的绑定,需要再创建一个 Local frame 进行存储
   - 执行完毕之后,将得到的结果一级一级往上反馈,最终得到最后结果:   - 执行完毕之后,将得到的结果一级一级往上反馈,最终得到最后结果:
 +\\ 
 {{ :cs:fundamental:cs61a:user_define_func.png?600 |}} {{ :cs:fundamental:cs61a:user_define_func.png?600 |}}
 \\ \\  \\ \\ 
行 184: 行 185:
   * 基于 //local frame// 的 name,是独立的;比如上例中的 ''x'',即便 name 相同,根据其所在的 local frame, 其绑定的 value 也不同。   * 基于 //local frame// 的 name,是独立的;比如上例中的 ''x'',即便 name 相同,根据其所在的 local frame, 其绑定的 value 也不同。
 ==Local names== ==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>