Contents

1_3_2CS61A 5,6,7,8(暂时告别,还有好多事,但是该复习了)

CS61A-5:Environments

Environments for Higher-Order

https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_1.png 这里讲解了一下它们新推出的diagram特性,比如这里定义了一个global frame然后在调用的时候又创建了一个frame1,讲解了整个的运算过程

Environments for Nested Definitions

https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_2.png 这里重新讲解了这个代码,当时我没听懂下来自己跑到这里来测试了,在第四节末尾笔记有体现[[1_3_1CS61A-1,2,3,4#^32405e|笔记链接]] https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_3.png 为什么我们需要有父级frame,因为这便于我们从子级向上找到环境下的变量值

Local Names

环境就是一系列的frame组成的工作条件 https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_4.png 这里介绍了当你离开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 https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_5.png 这里我们定义了一个f函数,一个g函数,查看environment diagram我们可以发现g函数的parent是global frame 我们从1到2查看y的值的时候,查不到y的值,便出现了报错name 'y'is not defined 这也就是local name 的作用域

Function Composition

https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_6.png 这里定义了复合函数,通过environment diagram我们可以看出这里有两个运行环境,一个是compose1运行的时候,另一个是make_adder这里有一个嵌套,所以运行的时候,先是运行make_adder(2)(3),然后运行错灭ose1(square,5)得出25

Lambda Expressions

https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_7.png 可以直接用lambda 定义一个函数而不使用def,并且不能有statemrnt https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_8.png lambda语句创建的函数一开始是没有名字的,通过square = lambda x: x * x 把函数赋值给square,def一开始就是把函数和名称绑定在一起的 def的函数有一个固有名称

Funtion Currying

https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_9.png 柯西化是一种对函数的操作,将双操作数的函数转化为单操作数的高阶函数

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

https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_10.png 我们再运行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显示不同,https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_11.png 而在vim里可以elif并列 https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_12.png 大概是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

https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_13.png lambda函数的作用域是它所在的创建的地方,所以上面的a=2仅限f内使用,在它之外是global的a=1 这里的第五行相当于嵌套了一个lambda函数来运行加法,所以得到f(g)的参数为1+1=2,然后在函数f中,a*g=2*2=4

Return

return作为函数的结尾,把值赋值给这个函数,可以调用函数来进入一个新的函数来执行函数体,这里建立了一个简单的搜索代码

Control

控制语句比如if,while https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_14.png 可以用if_这个自定义的函数来替代if语句,但是他们还是有差别 在call函数的之前会先对语句也就是这里的参数进行运算,会出现上下两图不同的结果,这也是为什么它们存在于语句中而不是以函数的形式存在,一些语句允许解释器跳过运算和操作步骤 https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_15.png

Control Expressions

讲了一下逻辑里面的and和or 也就是short-circuit

Abstraction

调用函数并运用的时候,不需要知道它是如何运行的,你只需要知道它做了什么,然后正确的调用,就像是api一样。 后面讲解了一些函数好的取名方式,重复的工作可以从中间抽取一部分出来编写成有意义的函数 https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_16.png 你不必完全按照要求做,因为编写程序是一种创造行为,只要是为了便于他人和自己理解或者其他的什么原因,让你觉得这样比较好,那你就直接那样做吧。

Errors & Tracebacks

三种错误

  1. 语法错误,在运行之前就可以检查出来,因为没有按照python规定的语法要求进行编写
  2. 报错,会提示runtime bug
  3. 运行结果不对,只有写测试找到bug 讲解了一些debug的方法 <<youtube BTGJ_esCPWI» 好气啊,很努力去上2学分的选修课了,最后老师还是只给了88分,南寿寺了

CS61A-8:Function Examples

Midterm 1 Review

  • https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_17.png print得到的效果
def delay(arg):
	print('delayed')
	def g():
		return arg
	return g

https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_18.png 这里delay(delay)()(6)() 调用了delay,delay的函数,然后被调用的1delay里面的参数为6,不是none所以会在解释器interpreter打印出来,也就是delayed,deplayed,6 https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_19.png 这里是print调用了delay,delay调用prit的值,然后调用4, 这里print里的delay的两个参数是print和(),然后(4)作为print的参数 https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_20.png 此处add里面的第一个参数,pirate(3)()(square)(4), 因为plunder函数里return的是最近帧的arggg,所以3对结果没有影响,arggg实际上是4的平方,16,然后16+1=17 https://mayue-1312060474.cos.ap-chengdu.myqcloud.com/picgo/1_3_2CS61A-5,6,7,8_image_21.png

Flag Counter