公开课学习日记

CS61A学习日记

2025.8.30

总算开始学CS61A了,祝我好运。
仅 Project 而言,正反馈真的很强,只需要按照要求把代码写完就行了,其他的已经被提供好了,真的是保姆级教程啊。

  • HW 01 done
  • Lab 00,01 done
  • Lecture 1,2,3,4 done
  • Hog Phase 1 done (Problem 0,1,2,3,4,5)
笔记

有趣的例子:输入 print(print(1),print(2)) 会输出

1
2
None None

这是因为非纯函数 print 会返回 None


Doctest 感觉很牛啊。现有如下代码( test.py ):

from operator import floordiv, mod


def divide_exact(n, d=10):
    """Return the quotient and remainder of dividing N by D.

    >>> q, r = divide_exact(2013, 10)
    >>> q
    201
    >>> r
    3
    """
    return floordiv(n, d), mod(n, d)

若运行指令 python -m doctest test.py -v 时,会返回如下信息:

Trying:
    q, r = divide_exact(2013, 10)
Expecting nothing
ok
Trying:
    q
Expecting:
    201
ok
Trying:
    r
Expecting:
    3
ok
1 item had no tests:
    test
1 item passed all tests:
   3 tests in test.divide_exact
3 tests in 2 items.
3 passed.
Test passed.

高阶函数,感觉很牛。

def cube(k):
    return pow(k, 3)


def summation(n, term):
    """
    >>> summation(5,cube)
    225
    """
    total, k = 0, 1
    while k <= n:
        total, k = total + term(k), k + 1
    return total

def make_adder(n):
    """
    >>> add_three=make_adder(3)
    >>> add_three(4)
    7
    """

    def adder(k):
        return k + n

    return adder

2025.8.31

这个可选的 Lecture 6 完全不知道在干嘛啊,大雾,就莫名其妙地听完了,之后可能再回过去看看吧

  • Lecture 5,6 done

2025.9.1

稀里糊涂地学函数式编程,稀里糊涂地写完了第一个 Project ,但正反馈还是很强的。
装饰器也是个很牛的东西,记一下。

  • Lab 02 done
  • Lecture 7,8,9,10,11,12 done
  • Hog done (Phase 2(Problem 6,7,8,9,10,11,12))
笔记

*args 语法表示传递给函数的所有参数, 然后,我们可以将 *args 传递给另一个函数,从而使用相同的参数调用该函数。请注意,柯里化阶段只是配置,不涉及 *args ,调用阶段才真正使用 *args 处理参数。

以 Problem 8 为例:

def make_averaged(original_function, samples_count=1000):
    """Return a function that returns the average value of ORIGINAL_FUNCTION
    called SAMPLES_COUNT times.

    To implement this function, you will have to use *args syntax.

    >>> dice = make_test_dice(4, 2, 5, 1)
    >>> averaged_dice = make_averaged(roll_dice, 40)
    >>> averaged_dice(1, dice)  # The avg of 10 4's, 10 2's, 10 5's, and 10 1's
    3.0
    """
    # BEGIN PROBLEM 8
    "*** YOUR CODE HERE ***"

    def f(*args):
        total = 0
        for _ in range(samples_count):
            result = original_function(*args)
            total += result
        return total / samples_count

    return f

    # END PROBLEM 8

假设我们有一个函数 say_hello

def say_hello():
    print("Hello!")

如果,我们想在执行这个函数前打印一条日志,比如 “Function is called” ,最直接的方法是修改这个函数。但如果有很多函数都需要这个功能,逐个修改就非常麻烦且容易出错。这时,我们就可以用装饰器的思想。

我们创建一个新函数 log ,它接受一个函数作为参数,并返回一个新的函数。

def log(func): # func 是要被装饰的函数,比如 say_hello
    def wrapper(): # 定义一个内部函数,它将“包装”原函数并添加新功能
        print(f"Function {func.__name__} is called") # 新增的日志功能
        return func() # 调用原始函数并返回其结果
    return wrapper # 返回这个包装后的新函数

现在,我们可以手动“装饰” say_hello 函数:

def say_hello():
    print("Hello!")

# 把 say_hello 传递给 log,log 返回 wrapper 函数
# 再将 wrapper 函数重新赋值给 say_hello
say_hello = log(say_hello)

# 现在调用 say_hello,实际上调用的是 wrapper 函数
say_hello()
# 输出:
# Function say_hello is called
# Hello!

Python 提供了 @ 符号作为装饰器的语法糖,让代码更简洁、更优雅。看到 @log 放在函数定义上面,我们就知道 say_hellolog 这个装饰器增强了。上面的手动赋值过程等价于:

@log # 这行代码等同于:say_hello = log(say_hello)
def say_hello():
    print("Hello!")

say_hello()

2025.9.2

HW 03 里有附加挑战题 Q6 ,涉及到了“Y-组合子”,就单纯记一下,感觉很夸张,应该也用不上。

  • HW 02,03 done
  • Lab 03 done
  • Cats Phase 1 done (Problem 0,1,2,3,4)

2025.9.3

看似两天,其实是一晚上写完了 Cats 。
数据抽象这个东西感觉确实挺抽象的,我听得似懂非懂感觉大概理解是个什么思想,后面学到 OOP 的时候关于会有更深刻的体会吧。
树这一课讲的也是真的抽象,神秘的表示方式看得我两眼一黑。

  • HW 04 done
  • Lab 04,05 done
  • Lecture 13,14,15,16 done
  • Cats done (Phase 2(Problem 5,6,7,8,9,10))
笔记

学到了怎么定义无穷大:

inf = float('inf')

简记一点列表的操作吧:

  1. 创建列表

基本创建

# 空列表
empty_list = []
empty_list = list()
# 有初始值的列表
numbers = [1, 2, 3, 4, 5]
fruits = ['apple', 'banana', 'orange']
mixed = [1, 'hello', 3.14, True]

二维/多维列表

# 2x3 二维列表
matrix = [
    [1, 2, 3],
    [4, 5, 6]
]
# 3x3x3 三维列表
cube = [
    [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
    [[10, 11, 12], [13, 14, 15], [16, 17, 18]],
    [[19, 20, 21], [22, 23, 24], [25, 26, 27]]
]
  1. 添加元素

append() - 在末尾添加单个元素

fruits = ['apple', 'banana']
fruits.append('orange')  # ['apple', 'banana', 'orange']

extend() - 在末尾添加多个元素

fruits = ['apple', 'banana']
fruits.extend(['orange', 'grape'])  # ['apple', 'banana', 'orange', 'grape']

insert() - 在指定位置插入元素

fruits = ['apple', 'banana']
fruits.insert(1, 'orange')  # ['apple', 'orange', 'banana']
  1. 删除元素

remove() - 删除指定值的元素

fruits = ['apple', 'banana', 'orange']
fruits.remove('banana')  # ['apple', 'orange']

pop() - 删除指定位置的元素并返回

fruits = ['apple', 'banana', 'orange']
removed = fruits.pop(1)  # removed = 'banana', fruits = ['apple', 'orange']

del - 删除指定位置的元素

fruits = ['apple', 'banana', 'orange']
del fruits[1]  # ['apple', 'orange']
  1. 访问元素

索引访问

fruits = ['apple', 'banana', 'orange']
print(fruits[0])   # 'apple' (第一个元素)
print(fruits[-1])  # 'orange' (最后一个元素)

切片访问

numbers = [0, 1, 2, 3, 4, 5]
print(numbers[1:4])   # [1, 2, 3] (索引1到3)
print(numbers[:3])    # [0, 1, 2] (前3个)
print(numbers[3:])    # [3, 4, 5] (从索引3开始)
  1. 修改元素
fruits = ['apple', 'banana', 'orange']
fruits[1] = 'grape'  # ['apple', 'grape', 'orange']
  1. 常用方法

len() - 获取长度

fruits = ['apple', 'banana', 'orange']
print(len(fruits))  # 3

index() - 查找元素索引

fruits = ['apple', 'banana', 'orange']
print(fruits.index('banana'))  # 1

count() - 统计元素出现次数

numbers = [1, 2, 2, 3, 2, 4]
print(numbers.count(2))  # 3

sort() - 排序

numbers = [3, 1, 4, 1, 5]
numbers.sort()  # [1, 1, 3, 4, 5] (升序)
numbers.sort(reverse=True)  # [5, 4, 3, 1, 1] (降序)

reverse() - 反转

numbers = [1, 2, 3, 4, 5]
numbers.reverse()  # [5, 4, 3, 2, 1]
  1. 列表操作

+ - 列表拼接

list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2  # [1, 2, 3, 4, 5, 6]

* - 列表重复

numbers = [1, 2, 3]
repeated = numbers * 3  # [1, 2, 3, 1, 2, 3, 1, 2, 3]

in - 检查元素是否存在

fruits = ['apple', 'banana', 'orange']
print('apple' in fruits)  # True
print('grape' in fruits)  # False
  1. 列表推导式
# 创建平方数列表
squares = [x**2 for x in range(5)]  # [0, 1, 4, 9, 16]
# 带条件的列表推导式
even_squares = [x**2 for x in range(10) if x % 2 == 0]  # [0, 4, 16, 36, 64]
# 二维列表推导式
matrix = [[i*j for j in range(3)] for i in range(3)]
# [[0, 0, 0], [0, 1, 2], [0, 2, 4]]
  1. 遍历列表
fruits = ['apple', 'banana', 'orange']
# 直接遍历元素
for fruit in fruits:
    print(fruit)
# 遍历索引和元素
for i, fruit in enumerate(fruits):
    print(f"索引 {i}: {fruit}")
# 遍历二维列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in matrix:
    for element in row:
        print(element, end=' ')
    print()

2025.9.4

又学到新东西了,生成器函数有点意思的。
目前为止学到的 OOP 感觉就是封装一个类,和之前的数据抽象类似的感觉。这个 Lab 的强度上来一点了,主要是要对着输出示例去推测代码咋写。

题外话,拿 Jupyter 去调试真好用吧。

  • HW 05 done
  • Lab 06 done
  • Lecture 17,18,19,20,21 done
笔记

生成器函数使用 yield 可返回单个值,而用 yield from 可迭代生成器函数并返回所有值。值得注意的是,生成器函数直接返回的是生成器对象,也就是迭代器。任何对象只要实现了以下两个方法,就是迭代器:

  • __iter__() - 返回迭代器自身
  • __next__() - 返回下一个值,如果没有更多值则抛出 StopIteration

2025.9.5

迭代器这一块属实有点不会做。以及终于知道为什么 python 里树和列表都那么抽象了,原来是因为没有指针。呃呃。

  • HW 05 done
  • Lecture 22,23,24,25 done
  • Ants Phase 1(Problem 0,1,2,3) done

2025.9.6

没忍住熬夜写 Ants 了。其实逐步实现一些功能以后,剩下的类似的功能也就是缝缝补补就行了,反而感觉这个 Project 没那么困难,比抽象迭代器、生成器好懂多了。
晚上又把剩下的也写完了,姑且先到此为止了。

  • Ants done (Phase 2,3(Problem 4,5,6,7,8,9,10,11,12,Optional1,Optional2))

CS188

2025.11.20

迷迷糊糊地听了 Intro ,看 Project 0 配置了一下环境,了解了一下 autograder ,虽然其实还是有点懵的。

  • Lecture 1 done
  • Project 0 done

2025.11.21

听着听着就睡着了,感觉这点东西都是大概了解的,但遇到各种新的名词还是有点云里雾里,一听一个不吱声。但看了 note 又感觉了解地清晰了一点,期待正式写 pacman 的时候。

  • Lecture 2,3,4 done

2025.11.22

依旧是看 lec 看不懂,看 note 才看懂。

  • Lecture 5 done

2025.11.23

和昨天一样,学得不多。

  • Lecture 6 done

2025.11.24

又学了两课,然后看代码看得想死,感觉没有一个很好的阅读代码的能力啊,仅仅是这样量级的代码我就已经在开始的时候觉得无从下手了,哎哎,先写个 Q1 大致熟悉一下。

笔记

创建一个空集合:

s = set()

注意不能写:

s = {}

因为这指的是创建一个空字典。

新语法:

def my_function(a: int, b: Tuple[int, int], c: List[List], d: Any, e: float=1.0):

这用于指定 Python 应该为该函数传入的参数类型。在下面的示例中, a 应该是一个整数( int ), b 应该是一个包含两个 int 的 tuple , c 应该是一个任意类型的 Lists 的 List (即一个二维数组), d 与未指定类型相同,可以是任何值, e 应该是一个 float )。如果没有传入任何参数, e 值也会被设置为 1.0 。

  • Lecture 7,8 done
  • Project 1 Q1 done

2025.11.25

猛然发现 sp24 和 fa18 的课程安排顺序上有差异,不过也无所谓了,依旧是听 lec 听睡过去了,之后补 note 再细读。至于代码实现,因为遇到了许多问题,所以笔记上倒是写了不少较为具体的内容。之后也多在笔记里写点东西吧,照这么看 lec 就当打发时间水过去了(划掉)。

笔记

集合去重且有 add 方法,没有 append 方法。 append 是列表( list )的方法,列表不会自动去重,查找是否已访问需要 \(O(n)\)

Python 的 list 是动态数组,调用 list.insert(i, x) 会在索引 \(i\) 位置前插入元素,并把后面的元素整体向右移动。所以 insert(0, item) 把新元素放在索引 \(0\)(头部),原先第 \(0\) 个元素移到索引 \(1\) ,依次类推,实现“头部插入”,时间复杂度 \(O(n)\)

UCS 需要记录当前最优累计代价,发现更低代价要重新开放节点。因为在非统一步代价环境中,第一次访问某节点的路径代价可能不是全局最优。若一旦把节点标记且之后不再访问,就可能丢失后来出现的更低价路径,最终产生错误解。实际上,UCS 本质即无负边权的 Dijkstra 算法。

创建字典并赋值有两种写法:

dict = {key: value}

等价于

dict = {}
dict[key] = value

前者是创建字典的同时放入键值,后者是先建空字典再赋值。

get 是 Python 字典 dict 的内置实例方法 dict.get(key, default=None) ,这表示:查找键 key 对应的值;若键不存在返回提供的 default(未提供则返回 None )。我们可以写 dict.get(key, float("inf")) ,这样在不存在时会返回 inf

A* 搜索的优先级是 \(g(n) + h(n)\) ,其中: \(g(n)\) 是起点到当前状态的真实累计代价, \(h(n)\) 是启发式估计当前状态到目标的代价。和 UCS 不同的是,我们在优先队列的优先级判断里使用 \(f\) ,在节点上依然只存储 \(g\)

  • Lecture 9 done
  • Project 1 Q2,3,4,5 done
posted @ 2025-08-30 10:42  haphyxlos  阅读(18)  评论(0)    收藏  举报