指定變數 == 一個有趣的問題: ``` a=10 b=15 c=a+b print(c) #25 a=0 print(c) #25 how to get c 15? ``` 要回答這個問題,首先就從最基本的變數指定來看 以 `golang`為例 ```go= a := 10 fmt.Printf("address of a : %v\n", &a) // address of a : 0xc00002c008 b := 15 fmt.Printf("address of b : %v\n", &b) // address of b: 0xc00002c050 c := a + b fmt.Printf("address of c : %v\n", &c) // address of c : 0xc00002c058 fmt.Printf("value of c : %d\n", c) // value of c : 25 a = 0 fmt.Printf("value of c : %d\n", c) // value of c : 25 ``` [看結果](https://play.golang.org/p/nDlashBlIfY) 從這邊我們可以看到,`a := 10`這個動作會在記憶體裡指定一個位址(`0xc00002c008`),並將10存放在這個位址,同理,b c 也是ㄧ樣 回頭來看這個問題。 第16行,當 a 這個變數改為0的時候,其實我們做的只是把`0xc00002c008`這個位址的值更改為0,理所當然的,它不可能同時去更動到`0xc00002c058`這個位址的值,因此 c 當然還是維持在 25 不變 那該怎麼印出 `c = 15` 呢? --- * 範例一 ```go= a := 10 fmt.Printf("address of a : %v\n", &a) // address of a : 0xc00002c008 b := 15 fmt.Printf("address of b : %v\n", &b) // address of b : 0xc00002c050 c := func() int { return a + b } fmt.Printf("value of c : %d\n", c()) // value of c : 25 a = 0 fmt.Printf("value of c : %d\n", c()) // value of c : 15 b = 5 fmt.Printf("value of c : %d\n", c()) // value of c : 5 ``` [看結果](https://play.golang.org/p/DSho8FtVJJ9) 這邊將 c 指定為一個 function,回傳 `a + b`,這樣一來不管 a b 怎麼改變,c 的回傳值還是可以綁定到 a b 的位址 --- * 範例二 最近工作需要,常常用到「觀察者模式」這個 pattern,所以就簡單設計一個**變數觀察者**的模型如下: ```go= // 定義觀察者介面 type observer interface { update(name string, num int) register(interface{}) } // 變數觀察者 type variableObserver struct { variables map[string]int name string num int } // 更新 func (vo *variableObserver) update(name string, num int) { vo.variables[name] = num vo.refresh() fmt.Printf("%s = %d\n", vo.name, vo.num) } // 刷新數值 func (vo *variableObserver) refresh() { vo.num = 0 for _, n := range vo.variables { vo.num += n } } // 註冊 func (vo *variableObserver) register(item interface{}) { v := item.(*variable) vo.variables[v.name] = v.num vo.refresh() } ``` 這邊為了簡化架構,所以觀察者介面只定義了`update`和`register`兩個行為,`variableObserver`變數觀察者為實作觀察者介面的物件,所有註冊的變數會存在內部的一個`map`裡,`update`會更新指定的變數並透過`refresh`刷新本身的數值 接著定義**變數** ```go= // 變數 type variable struct { name string num int observer } // 註冊 func (v *variable) register(o observer) { v.observer = o o.register(v) } // 通知 func (v *variable) notify() { v.observer.update(v.name, v.num) } // 更新 func (v *variable) update(n int) { v.num = n fmt.Printf("%s updates to %d\n", v.name, v.num) v.notify() } ``` 一樣為了簡化,每個變數都只有一個觀察者,透過`register`註冊綁定,並實作`update`更新變數的值,同時通知觀察者同步更新 [看結果](https://play.golang.org/p/vf_5wgRz_wD) --- 這個問題其實很不錯,非常適合拿來和初學者解釋變數和基本型別 延伸閱讀: [python中的變數](https://openhome.cc/Gossip/Python/Variable.html) [觀察者模式](https://openhome.cc/Gossip/DesignPattern/ObserverPattern.htm) [Reactive Programming](https://weihanglo.tw/posts/2017/intro-rx-0-reactivex/) [Reactivex.io](http://reactivex.io/)