SICP 課程總結 & 複習
SICP 課程總結 & 複習
小作文
有賴於那個終極的、偉大的、命定的教務系統,我選上了這門課:SICP,Structure and Interpret of Computer Programs,計算機程式的構造與解釋。
作為一門程式設計課,SICP頗有一種包羅永珍的氣質:講授三種程式語言;涉及一眾程式設計正規化;更由於馮新宇、李樾兩位老師都是程式設計語言(Programming Languages)界的大牛,這門課同樣包含了許多考試範圍外的 PL 概念。如果你常常在課程群裡提問,甚至還能瞭解到如何證明TSP可以在多項式時間內驗證之類的問題。總之是洋洋大觀。
由於上課的時候說到許多概念都是英文,所以具體內容我也用英文寫一下吧(
The curriculum SICP concentrates on the idea of abstraction, using Python, Scheme and SQL three languages to reveal various programming paradigm including imperative, declarative, functional, object-oriented and generic programming, and to cover numerous advanced concepts like higher-order function, closure, macro, polymorphism, metaclass and so on.
一般的評論都認為南大的SICP是一門實驗性、革新性的課程。
先說實驗性:
首先,今年也就剛剛是SICP第二年開課。按馮新宇老師的比喻,我們也就算是黃埔二期生,很有實驗性。
其次,計算機系的後續課程都是基於C/C++的,而SICP很有實驗性色彩地,全然不涉及C/C++內容。因此,雖然SICP和程式設計基礎(講授C/C++的傳統課程)是二選一的平行課,基本沒有學生會單選SICP,應該也只有我這樣的轉專業抽籤倒黴蛋例外了。
如果還要再說一點的話,SICP破天荒地用了五位助教。五位助教老師一方面高山仰止、盡職盡責,一方面和大家打成一片,騷話連篇。幾位助教在群裡耐心解答各類問題,還會額外地講一些型別系統、複雜度分析的知識,進一步擴充了SICP的課程內容,總之就是非常地好。
然後是革新性:
革新性其一就在之前說到的課程內容。據說SICP涉及的一些概念,比如閉包(closure),在南大甚至國內高校的其他所有課程中都是不會講到的。而且就程式設計教育而言,面向無基礎初學者的SICP,竟然涉及瞭如此多難懂甚至晦澀的概念,縱然是蜻蜓點水、淺嘗輒止式地涉及,對初學者來說無疑是一種挑戰。
革新性其二,在於我們這門課的兩位老師:馮新宇、李樾。兩位在 PL 屆都是赫赫有名的大佬。據說國內計算機繫有 PL 方向的高校都是鳳毛麟角,按樾哥的說法,讓他倆來講課也多少有宣傳PL方向的意思。
期中以前的內容
寒假想起來再寫吧,期末不考啊(
期中以後的內容
Lecture-15: Python Inheritance
Attributes: Look up & Assignment
簡單說來,Attribute 有兩個型別:class attribute & instance attribute。
做 Attribute Look up 時,會優先找 instance attribute,找不到時再找 class attribute,如果還是沒有找到,就繼續上訴到父類,直到找到為止。如果最終還是找不到,就返回一個 attribute error 。
做 Attribute Assignment 時,考慮 dot expression 的兩個部分:
<expression>.<name>
如果 <expression>
是一個 instance,那麼就建立或修改對應的 instance attribute
如果 <expression>
是一個 class,那麼就建立或修改對應的 class attribute
當然,class attribute 和 instance attribute 是有可能重名的,於是情況就會變得稍迷惑一些。
考點都在這個例子裡,大概:
class Account:
interest = 0.02
def __init__(self, name_str):
self.name = name_str
jim_account = Account('Jim')
tom_account = Account('Tom')
###################################
>>> tom_account.interest
0.02
>>> jim_account.interest
0.02
>>> Account.interest = 0.04
>>> tom_account.interest
0.04
>>> jim_account.interest
0.04
>>> jim_account.interest = 0.08
>>> tom_account.interest
0.04
>>> jim_account.interest
0.08
>>> Account.interest = 0.05
>>> tom_account.interest
0.05
>>> jim_account.interest
0.08
Inheritance, Multiple Inheritance
Inheritance 寫法上很簡單:
class <Name>(<Base Class>):
<suite>
子類會顧名思義地繼承父類的 class attributes,包括所有的 methods。
可以在 <suite>
部分任意的覆寫父類的內容,也當然可以加入新的。沒有覆寫的attributes一律預設使用父類的。
Multiple Inheritance 同樣很容易寫:
class <Name>(<Base1>, <Base2>, ...):
<suite>
當然,這裡不可避免地會涉及 Diamond Problem。Python的處理辦法是Method Resolution Order,說實話我覺得相當迷惑。還是 C++ 寫起來安心(
樾哥提到 Python 並不適合大規模的開發使用,寫一點精短的小程式的話,這樣的設計應該也還受用吧。
這個應該不是重點。
Quiz
難倒是不難,但是這是人寫的程式碼嗎……
class A:
z = -1
def f(self, x):
return B(x - 1)
class B(A):
n = 4
def __init__(self, y):
if y:
self.z = self.f(y)
else:
self.z = C(y + 1)
class C(B):
def f(self, x):
return x
Quesion:
-
>>> C(2).n ???
-
>>> a.z == C.z ???
-
>>> a.z == b.z ???
-
Which evaluates to an integer?
b.z b.z.z b.z.z.z b.z.z.z.z
答案是 4, True, False, b.z.z.z
Extensions:
-
The definition of expressions
-
Metaclass, duck type and other things about type system
Python中的
type
就是一個 metaclass ,也就是可以建立其它 class 的,一種更加形而上的 class。>>>type(jim_account) <class 'Account'> >>>type(Account) <class 'type'>
而如果你試圖用 Python 研究型別系統,得到
>>>type(type) <class 'type'>
也不必驚訝,這是 duck type 的緣故,參見
-
Inheritance, composition and mixin
Inheritance 是個很恐怖的東西。課件裡寫到,Inheritance helps code reuse but NOT for code reuse。繼承的有兩個主要的壞處,一是破壞封裝,你不得不上溯父類;二是可能會繼承冗餘的資訊。我印象中網上關於繼承和耦合性還有很多有意思的釣魚文。
實際上在做和 class 有關 code reuse 的時候,inheritance, composition 和 mixin 都是可能的選項。
可以參見
https://naildrivin5.com/blog/2012/12/19/re-use-in-oo-inheritance.html
說實話mixin的理解還是有點難度的。
-
Diamond Problem
Lecture-16: Special Methods
Special Methods
簡單地說就是兩個函式 str()
和 repr()
,傳進某個 instance 就會給出一個顯式的字串表達。
其中 str()
的返回結果更加 human readable,而 repr()
很大程度上是給直譯器看的。
譬如說
>>> from fractions import Fraction
>>> half = Fraction(1, 2)
>>> repr(half)
'Fraction(1, 2)'
>>> str(half)
'1/2'
特別地,如果沒有實現 str()
的話呼叫會預設返回 repr()
的結果。
這兩個函式就有多型( Polymorphism )的性質。
實現上很簡單:
class Ratio:
def __init__(self, numerator, denominator):
self.n = numerator
self.d = denominator
def __repr__(self):
return "Ratio({0}, {1})".format(self.n, self.d)
def __str__(self):
return "{0}/{1}".format(self.n, self.d)
實現過載操作符也是類似可行的:
class Ratio:
def __init__(self, numer, denom):
from math import gcd
self.n, self.d = numer // gcd(numer, denom), \
denom // gcd(numer, denom)
def __repr__(self):
return "Ratio({0}, {1})".format(self.n, self.d)
def __str__(self):
return "{0}/{1}".format(self.n, self.d)
def __add__(self, other):
self = Ratio(self.n * other.d + self.d * other.n,\
self.d * other.d )
過載操作符的時候常常會遇到 <class A> + <class B>
能用,<class B> + <class A>
就不行了的情況。這時候只需要新增一行 __radd__ = __add__
就可以了。
Extensions
-
Polymorphism
分成三類 Ad Hoc Polymorphism, Parametric Polymorphism & Inclusion Polymorphism。課間上破天荒給了C++程式碼示例:
// Ad Hoc Polymorphism foo(int) {} foo(string) {} // Parametic Polymorphism Template<typename T> T foo(T x, T y) { return (x > y)? x: y; } foo<int>(3, 7) foo<char>('h', 'k') // Inclusion Polymorphism T v; // T has many types v.foo();
在這恐怖的課堂看見C++,就像回家了一樣(
參見
https://en.wikipedia.org/wiki/Polymorphism_(computer_science)
Lecture-17: Linked Lists & Trees
就是實現兩個類:連結串列和樹
感覺沒啥可寫的,這節課說實話沒啥資訊量,放在這裡應該是做一個 class 和 OOP 的總結,然後引入連結串列給 Scheme 開個頭吧。