函數式編程Smalltalk

這篇文章是爲了熟悉,函數式編程中的消息傳遞


安裝

https://github.com/mcandre/gst-wingit

下載安裝,而後打開 cmd(不能使用 git bash,緣由未知),輸入 gst 便可開始 Smalltalk 之旅

image.png

語法

1. (1 + 2) * 3 // 9
2. 'Hello, world' printNl //'Hello, world' (必須是單引號)
3. Array new: 20 (建立一個數組:長度20)
4. x := Array new: 20 (建立長度20的數組,並賦值給x)!
image.png
5. x at: 1 put: 99 (改變x的數組第一項,值爲99)
image.png
6. (x at: 1) + 1 (把x裏面第一項取出來,加上1,x自己不變)
image.png
7. x := Set new (建立一個Set)
image.pnggithub

8. x add: 5. x add: 7. x add: 'foo' (往Set裏添加)
  x add: 5; add: 7; add: 'foo' (簡寫)

image.png
9. x remove: 5 (刪除其中的一條)
image.png
10. x includes: 5 (是否存在這條)
image.png
11. y := Dictionary new (建立一個字典)
image.png
12. y at: 'One' put: 1 (往y的這個字典裏面放1)
image.png編程

以前我一直搞不懂爲何有些人說 person.cut('xxx') 是數組

  1. 給 persom 對象發送了一個 cut 消息
  2. person 對象會響應這個消息

咱們學js的時候理解,這就是一個函數調用,不懂爲何會這麼說.
可是,改爲 Smalltalk 就理解了bash

person cut: 'xxx' (給person傳遞一個消息,切掉xxx)
person cut: 'xxx'; cut: 'hands' (給person傳遞多個消息)

面向對象的核心就是對象與對象之間交互。閉包

  1. 對象維護本身的狀態和生命週期
  2. 每一個對象獨立
  3. 對象和對象直接經過消息傳遞來工做

如今想一想這個名字仍是頗有意思的Smalltalk:說小話,好像是上課傳紙條併發

面向對象與函數式的關係

請帶着以下問題來看下面的文章框架

  1. 面向對象和函數式是對立的(不可融合的)嗎? //不對立
  2. 二者的優缺點是什麼?

經過一個取錢的程序來看上面的問題


// 建立一個 money變量 和 take函數(set! set是設置這個值,!表明當前可使用set)
(define money 100)
(define (take n)
    (set! money (- money n))
    money
)

能夠將 money 改成局部變量模塊化

(define taker
    (let (money 100)
        (lambda (n)
            (set! money (- money n))
            money)
    )
(taker 25)
75
(taker 25)
50

或者函數式編程

(define (taker money)
    (lambda (n)
        (set! money (- money n))
        money)
(define taker1 (taker 100))
(define taker2 (taker 100))
(taker1 50)
50
(taker2 70)
30
  1. 一樣一個函數,每次執行的結果卻不同。
  2. 閉包能夠存狀態,不一樣的函數有各自的狀態

函數式也能夠作成消息傳遞風格

使用消息傳遞風格就能夠構造 account 對象了,account 對象能夠響應 withdraw(取錢)和 deposit(存錢)消息:

// 函數money內部返回的是dispatch函數
let makeAccount = money => {
  let take = (n) => {
    money = money - n
    return money
  }
  let save = (n) => {
    money = money + n
    return money
  }
  let dispatch = (m) => {
    return (
    m === 'take' ? take :
    m === 'save' ? save :
    new Error('unknown request'))
  }
  return dispatch
}

接下來是使用 makeAccount 創造兩個 account 對象(實際上是過程):

let account1 = makeAccount(100)
account1('take')(70) // 30
account1('save')(50) // 80

寫成 Lisp 其實更像是消息傳遞

((account1 'withdraw) 50)
((account1 'deposit ) 50)

以上只是利用『分派』模式來模擬對象,函數式編程有完整的面向對象體系,你們有興趣能夠自行了解。

賦值的利弊

引用原文的一句話:

將賦值引進程序設計語言,將會使咱們陷入許多困難概念的叢林中。

可是它就沒有好處嗎?
好處: 每一個對象能夠存儲本身的狀態,封裝的更完全,不須要關心數據會怎麼變,由於這個對象會維護這個數據

與全部狀態都必須顯示地操做和傳遞額外參數的方式相比,經過引進賦值以及將狀態隱藏在局部變量中的技術,能讓咱們以一種更**模塊化**的方式構造系統。

可是這本書立刻又加了一句話:

惋惜的是,咱們很快就會發現,事情並非這麼簡單。

賦值的代價

1. 賦值操做使咱們能夠去模擬帶有局部狀態的對象,可是,這是有代價的,它是咱們的程序設計語言不能再用代換模型來解釋了。進一步說,任何具備「漂亮」數學性質的簡單模型,都不可能繼續適合做爲處理對象和賦值的框架了。  
2. 只要咱們不使用賦值,一個「過程/函數」接收一樣的參數必定會產生一樣的結果,所以就能夠認爲這個「過程/函數」是在計算「數學函數」。  
3. 不用任何賦值的程序設計稱爲函數式程序設計。

示例

pager 組件的頁碼生成過程 看代碼的時候必定要考慮有沒有賦值(一個變量第一次出現等於號是定義,當被定義的改變叫作賦值)

賦值的本質

1. 若是沒有賦值,money 只不過就是一個值的名字;  
2. 若是有了賦值,money 就是一個容器,能夠保存不一樣的值。
3. 這帶來的問題不少。普遍採用賦值的程序設計叫作『命令式/指令式』程序設計。
4. 命令式程序在遇到『併發』『克隆』等問題時常常很使人頭疼。
5. 舉例說明一下。

函數式編程的特色

  1. 數學!(公理化和可證實)
  2. 更增強調程序執行的結果而非執行的過程
  3. 函數是一等公民(函數能夠做爲參數--高階函數)
  4. 純函數,拒絕反作用(也就是賦值)
  5. 不可變數據
  6. 數據即代碼,代碼即數據(本課沒有涉及)
  7. 引用透明(本課沒有涉及)
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息