SICP 第三章的標題是:模塊化、對象和狀態。我在這一章找到了「函數式程序設計」的定義(見知識點五),哈哈真是一本神書。編程
開篇的一段話十分吸引我,這段話在高層次上說明了面向對象編程的缺點,以及 Rx.js 這種編程範式的優勢。數據結構
有一種很是強有力的設計策略,特別適合於構造那些模擬真實物理系統的程序,那就是「基於被模擬的系統的結構去設計程序的結構」。併發
在這一章裏,咱們要研究兩種特色很鮮明的組織策略,它們源自對於系統結構的兩種很是不一樣的世界觀。第一種策略將注意力集中在「對象」身上,將一個大型系統當作一大批對象,他們的行爲可能隨着時間的進展而不斷變化。第二種策略將注意力集中在流過系統的信息流上,很是像電子工程師觀察一個信號處理系統。框架
基於對象的途徑和基於流處理的途徑,都對程序設計提出了具備重要意義的語言要求。模塊化
對於對象途徑而言,咱們必須關注計算對象能夠怎樣變化而又同時保持其標識。這將迫使咱們拋棄老的計算的代換模型,轉向更機械式的、理論上也更不容易把握的環境模型。在處理對象、變化和標識時,各類困難的根源都在於咱們須要在這一計算模型中與時間搏鬥。若是容許程序併發執行的話,事情就會變得更困難。函數
對於流方式來講,它特別可以用於鬆解在咱們的模型中對時間的模擬和計算機求值過程當中的各類事件的發生順序。咱們將經過延時求值作到這一點。post
考慮一個取錢的函數 withdrawspa
// 初始金額 100
> withdraw(25)
< 75 // 餘額 75
> withdraw(25)
< 50
> withdraw(25)
< Error: 餘額不足
> withdraw(15)
< 35
複製代碼
一樣一個函數,每次執行的結果卻不同。第一章的代換模型再也不有用了。設計
withdraw 應該如何用「過程」實現呢?記得嗎,以前咱們說過也許數據結構均可以用「過程」實現。code
let withdraw = (() => {
let balance = 100
return (amount) => {
if(amount <= balance){
balance = balance - amount
return balance
}else{
throw new Error('餘額不足')
}
}
})()
複製代碼
這一句 balance = balance - amount 是本書首次出現的對一個量進行賦值的語句(這裏 let balance = 100 是「初始化」不是「賦值」, balance 的第二次賦值纔是「賦值」)。
Scheme 語言裏賦值的語法是
set! balance (- balance amount)
複製代碼
賦值被設計成 set! ,足見 Scheme 對賦值的厭惡。
咱們用 makeWithdraw 來建立兩個 withdraw,會發現它們兩個的狀態是互不相干的:
let makeWithdraw = (balance) => (amount) => {
if(amount < balance){
balance = balance - amount
return balance
}else{
throw new Error('餘額不足')
}
}
複製代碼
下面是兩個 withdraw 的行爲:
let withdraw1 = makeWithdraw(100)
let withdraw2 = makeWithdraw(100)
withdraw1(50) // 50
withdraw2(70) // 30
複製代碼
使用消息傳遞風格就能夠構造 account 對象了,account 對象能夠響應 withdraw(取錢)和 deposit(存錢)消息:
let makeAccount = balance => {
let withdraw = (amount) => {
if(amount < balance){
balance = balance - amount
return balance
}else{
throw new Error('餘額不足')
}
}
let deposit = (amount) => {
balance = balance + amount
return balance
}
let dispatch = (m) => {
return (
m === 'withdraw' ? withdraw :
m === 'deposit' ? deposit :
new Error('unknown request'))
}
return dispatch
}
複製代碼
接下來是使用 makeAccount 創造兩個 account 對象(實際上是過程):
let account1 = makeAccount(100)
account1('withdraw')(70) // 30
account1('deposit')(50) // 80
寫成 Scheme 其實更像是消息傳遞
((account1 'withdraw) 50)
((account1 'deposit ) 50)
複製代碼
將賦值引進程序設計語言,將會使咱們陷入許多困難概念的叢林中。
可是它就沒有好處嗎?
與全部狀態都必須現實地操做和傳遞額外參數的方式相比,經過引進賦值以及將狀態隱藏在局部變量中的技術,能讓咱們以一種更___模塊化___的方式構造系統。
可是這本書立刻又加了一句話:
惋惜的是,咱們很快就會發現,事情並非這麼簡單。
接下來就進入了「賦值的代價」小節:
賦值操做使咱們能夠去模擬帶有局部狀態的對象,可是,這是有代價的,它是咱們的程序設計語言不能再用代換模型來解釋了。進一步說,任何具備「漂亮」數學性質的簡單模型,都不可能繼續適合做爲處理對象和賦值的框架了。
只要咱們不使用賦值,一個「過程」接收一樣的參數必定會產生一樣的結果,所以就能夠認爲這個「過程」是在計算「數學函數」。
不用任何賦值的程序設計稱爲函數式程序設計。
未完待續。
第二章的筆記:juejin.im/post/5c02ca…