1_3_2CS61A 5,6,7,8(暂时告别,还有好多事,但是该复习了)
CS61A-5:Environments
Environments for Higher-Order
这里讲解了一下它们新推出的diagram特性,比如这里定义了一个global frame然后在调用的时候又创建了一个frame1,讲解了整个的运算过程
Environments for Nested Definitions
这里重新讲解了这个代码,当时我没听懂下来自己跑到这里来测试了,在第四节末尾笔记有体现[[1_3_1CS61A-1,2,3,4#^32405e|笔记链接]] 为什么我们需要有父级frame,因为这便于我们从子级向上找到环境下的变量值
Local Names
环境就是一系列的frame组成的工作条件 这里介绍了当你离开pythontutor如何自己画environment diagram的方法,有时候这能帮你理解复杂的程序。 不一定对,但是还是按照自己的理解写一下吧: 我们定义一个函数的时候,就创建了一个frame,它的parent就是globle frame,我们除了嵌套定义的函数,都会出现在一个global frame里 global frame不会和值绑定 当它被调用的时候,就创建了一个local frame来运行,当local frame里出现函数调用的时候,会创建另一个frame,它的parent就是调用它的local frame,这便于我们从子级到parent逐级找到变量值
比如上面的代码我们创建了一个嵌套的函数体,所以从上到下有三个frame:global frame;f1;f2
这里我们定义了一个f函数,一个g函数,查看environment diagram我们可以发现g函数的parent是global frame
我们从1到2查看y的值的时候,查不到y的值,便出现了报错name 'y'is not defined
这也就是local name 的作用域
Function Composition
这里定义了复合函数,通过environment diagram我们可以看出这里有两个运行环境,一个是compose1运行的时候,另一个是make_adder这里有一个嵌套,所以运行的时候,先是运行make_adder(2)(3),然后运行错灭ose1(square,5)得出25
Lambda Expressions
可以直接用lambda 定义一个函数而不使用def,并且不能有statemrnt
lambda语句创建的函数一开始是没有名字的,通过square = lambda x: x * x
把函数赋值给square,def一开始就是把函数和名称绑定在一起的
def的函数有一个固有名称
Funtion Currying
柯西化是一种对函数的操作,将双操作数的函数转化为单操作数的高阶函数
CS61A-6:Sound
Function Example:Sound
为什么需要higher-order function?而不是依次输入参数到函数里 这里讲到一个关于声音的例子,创建了一系列的函数,并且利用这些函数来论证原因。
from wave import open
from struct import Struct
from math import floor
frame_rate = 11025
def encode(x):
"""Encode float x bewteen -1 and 1 as two bytes.
(See https://docs.python.org/3/library/struct.html)
"""
i = int(16384 * x)
return Struct('h').pack(i)
def play(sampler, name='song.wav', seconds=2):
"""Write the output of a sampler function as a wav file.
"""
out = open(name, 'wb')
out.setnchannels(1)
out.setsampwidth(2)
out.setframerate(frame_rate)
t = 0
while t < seconds * frame_rate:
sample = sampler(t)
out.writeframes(encode(sample))
t = t + 1
out.close()
def tri(frequency, amplitute=0.3):
"""A continuous triangle wave"""
period = frame_rate // frequency
def sampler(t):
saw_wave = t / period - floor(t / period + 0.5)
tri_wave = 2 * abs(2 * saw_wave) - 1
return amplitute * tri_wave
return sampler
c_freq = 261.63
我们再运行play(c)就会把c波形写成一个wave文件,这就是三角波形的声音
![[1_3_2CS61A-5,6,7,8_audio_1.wav]]
我们添加其他的音调
c_freq, e_freq, g_freq = 261.63, 329.63, 392.00
接着添加代码来创造声音
def both(f,g)
return lambda t: f(t) + g(t)
play(both(tri(c_freq),tri(e_freq)))
现在我们得到了c和e的复合声音 ![[1_3_2CS61A-5,6,7,8_audio_2.wav]] 我们还想要一个函数,只需要再规定的时间内播放声音,添加代码
def note(f, start, end):
def sampler(t):
seconds = t / frame_rate
if seconds < start:
return 0
elif seconds > end:
return 0
else:
return f(t)
return sampler
c, e = tri(c_freq), tri(e_freq)
play(note(c, 0, 1/4))
我们得到了一个只播放1/4 s的声音
![[1_3_2CS61A-5,6,7,8_audio_3.wav]]
我们如果想要两个哔声,把play的代码改成play(both(note(c, 0, 1/4),note(e,1/2,1)))
,现在我们得到了一个c和一个e
![[1_3_2CS61A-5,6,7,8_audio_4.wav]]
它听起来有一点刺耳,因为不像是正常发出来的声音,所以我们添加一个淡入淡出的功能,将note函数改为
def note(f, start, end, fade=0.1):
def sampler(t):
seconds = t / frame_rate
if seconds < start:
return 0
elif seconds > end:
return 0
elif seconds < start + fade:
return (seconds - start) / fade * f(t)
elif seconds > end - fade:
return (end - seconds) / fade * f(t)
else:
return f(t)
return sampler
这里遇到问题,缩进在vscode里面和vim显示不同, 而在vim里可以elif并列 大概是vscode本身的原因,在vscode里后面两个elif是在前一个elif的条件下,所以需要多缩进,它的tab应该是灵活变通的有时候是4个空格,有时候是8个空格,来自动适应python解释器 遇见缩进问题的时候我们可以交给ai处理,也可以放进记事本查看文件本来的样子 好了运行结果出来![[1_3_2CS61A-5,6,7,8_audio_5.wav]] 竟然有一种吹奏乐器的感觉了
我们再添加一些内容,制作一个2s的音乐
g, low_g = tri(g_freq), tri(g_freq / 2)
#z来判断我们播放到哪里
z = 0
song = note(e, z, z + 1/8)
z += 1/8
song = both(song, note(e, z, z + 1/8))
z += 1/4
song = both(song, note(e, z, z + 1/8))
z += 1/4
song = both(song, note(c, z, z + 1/8))
z += 1/8
song = both(song, note(e, z, z + 1/8))
z += 1/4
song = both(song, note(g, z, z + 1/4))
z += 1/2
song = both(song, note(low_g, z, z + 1/4))
z += 1/2
play(song)
这是结果![[1_3_2CS61A-5,6,7,8_audio_6.wav]] 打包函数并调用
def mario_at(octave):
c, e = tri(octave * c_freq), tri(octave * e_freq)
g, low_g = tri(octave * g_freq), tri(octave * g_freq / 2)
return mario(c, e, g, low_g)
def mario(c, e, g, low_g):
#z来判断我们播放到哪里
z = 0
song = note(e, z, z + 1/8)
z += 1/8
song = both(song, note(e, z, z + 1/8))
z += 1/4
song = both(song, note(e, z, z + 1/8))
z += 1/4
song = both(song, note(c, z, z + 1/8))
z += 1/8
song = both(song, note(e, z, z + 1/8))
z += 1/4
song = both(song, note(g, z, z + 1/4))
z += 1/2
song = both(song, note(low_g, z, z + 1/4))
z += 1/2
return song
play(mario_at(1/2))
![[1_3_2CS61A-5,6,7,8_audio_7.wav]]
CS61A-7:Functional Abstraction
Lambda Function Environments
lambda函数的作用域是它所在的创建的地方,所以上面的a=2仅限f内使用,在它之外是global的a=1 这里的第五行相当于嵌套了一个lambda函数来运行加法,所以得到f(g)的参数为1+1=2,然后在函数f中,a*g=2*2=4
Return
return作为函数的结尾,把值赋值给这个函数,可以调用函数来进入一个新的函数来执行函数体,这里建立了一个简单的搜索代码
Control
控制语句比如if,while 可以用if_这个自定义的函数来替代if语句,但是他们还是有差别 在call函数的之前会先对语句也就是这里的参数进行运算,会出现上下两图不同的结果,这也是为什么它们存在于语句中而不是以函数的形式存在,一些语句允许解释器跳过运算和操作步骤
Control Expressions
讲了一下逻辑里面的and和or 也就是short-circuit
Abstraction
调用函数并运用的时候,不需要知道它是如何运行的,你只需要知道它做了什么,然后正确的调用,就像是api一样。 后面讲解了一些函数好的取名方式,重复的工作可以从中间抽取一部分出来编写成有意义的函数 你不必完全按照要求做,因为编写程序是一种创造行为,只要是为了便于他人和自己理解或者其他的什么原因,让你觉得这样比较好,那你就直接那样做吧。
Errors & Tracebacks
三种错误
- 语法错误,在运行之前就可以检查出来,因为没有按照python规定的语法要求进行编写
- 报错,会提示runtime bug
- 运行结果不对,只有写测试找到bug 讲解了一些debug的方法 <<youtube BTGJ_esCPWI» 好气啊,很努力去上2学分的选修课了,最后老师还是只给了88分,南寿寺了
CS61A-8:Function Examples
Midterm 1 Review
- print得到的效果
def delay(arg):
print('delayed')
def g():
return arg
return g
这里delay(delay)()(6)() 调用了delay,delay的函数,然后被调用的1delay里面的参数为6,不是none所以会在解释器interpreter打印出来,也就是delayed,deplayed,6 这里是print调用了delay,delay调用prit的值,然后调用4, 这里print里的delay的两个参数是print和(),然后(4)作为print的参数 此处add里面的第一个参数,pirate(3)()(square)(4), 因为plunder函数里return的是最近帧的arggg,所以3对结果没有影响,arggg实际上是4的平方,16,然后16+1=17