2014-05-09

成為一個努力的人 -- 我的 MOOCs 之旅

記得在去年底的時候,我對自己許了一個新年願望:希望在新的一年,每一天都可以覺得比昨天的自己又更進步了,就如同網路上流傳的一句話「每天進步0.01!一年後你就比現在強大36倍!」。是的,我想成為一個努力的人,我想透過一連串的習慣與改變,讓自己更努力。

不過說真的,要一直努力,其實不是一件簡單的事,通常遇到瓶頸或倦怠時,我都用這兩句話來砥礪我自己,讓我繼續走下去。
  1. 「大部分人努力程度之低,根本輪不到拼天賦」
  2. 「最可怕的事情是,比你優秀的人比你更努力」
真的,其實努力的人沒那麼多,但同樣的,永遠有比你更努力的人在。

那要怎麼努力呢?其中一個我想要的方向是:活到老學到老,坦白說,我覺得絕大多數人終其一生學習的巔峰大概是出社會的那瞬間,也就是離開學校就停了,不妨思考一下,你覺得你有比畢業的那時多進步了什麼嗎?甚至在每一年結束的時候,都反思這一年到底累積了什麼,是值得的嗎?還是只是虛度了一年。而以我現在這個年紀來說,我覺得最重要的就是學習,人生是長期而持續的累積,大學研究所不過是 4 ~ 8 年,而剩下的日子少說也有 40 年,難道要這樣停滯不前嗎?

在學習上,我選擇做的事主要有兩個,第一是看書,很幸運的,看書對我來說並不是一件難事(雖然我看的並不快)。曾經看過一個統計,台灣人平均每年只讀兩本書,我馬上查了 anobii 上的紀錄,從 2009 ~ 2013 五年來,我平均每年大概看了 60 本(雖然估計有 9 成是小說…)。讀書其實是一種用進廢退的技能,是有加成效益的,當你習慣看大量的書,你就越能夠看更多的書。所以我想我應該是在不知不覺中有了這樣的習慣,真要追本溯源的話,我猜是在高中上數學課,卻都在下面偷看小說練出來的。

而另一件事,是上 Coursera,這大概是我今年我最喜歡的一個改變。Coursera 是著名的 MOOC (Massive open online course)之一,其他的還有 edX、udacity 等多種平台,很多人談論過 MOOC 的出現會對這世界的教育產生怎樣的改革,不過我不知道那些人是不是真的有修完過一門課,所以我想說說就我的親身體驗,在接觸 Coursera 一年多以來,有怎樣的心得。

我大概是從去年四月多(離開 M 公司之後 XD)開始玩 Coursera,到 2013 年底一共非常踏實的跟完這六門課,作業考試都有,也有最後的 certification,這個數量並不多,但已足夠讓我養成上課的習慣。
  1. Algorithms: Design and Analysis Part 1, Stanford, 2013
  2. Algorithms: Design and Analysis Part 2, Stanford, 2013
  3. Algorithms Part 1, Princeton, 2013
  4. C++ For C Programmers, University of California, Santa Cruz, 2013
  5. Learn to Program: The Fundamentals, University of Toronto, 2013
  6. An Introduction to Interactive Programming in Python, Rice University, 2013
一開始我都修 computer science 的,然後到了今年,我就開始亂看:Creativity、Irrational Behavior、Marketing、Leadership,不同的領域會有不同的視野。說真的,我覺得 MOOC 真的很酷,跟在學校上課有許多的不同。

量變形成質變:每一門課都是上萬人在修,討論區會非常的熱烈,而且品質很高,這完全不是傳統大學課程可以做到的。另外,修課的人也不僅僅是學生,像 computer science 的課就會有很多高手隱藏在其中,會提供很多很棒的見解。

跟隨大師:在上 Princeton 的 Algorithms 時,我真的很驚訝,是 Robert Sedgewick 耶,小時候都看他寫的書,沒想到竟然可以直接聽他講課,而且影片、講義、作業水準都很高。

跨領域:想學什麼就看什麼,可以很輕易的就接觸到自己以前領域外的知識,而且不會有像學校那種去別系聽課,在圈圈外的感覺,因為 Coursera 上的學生組成根本就是個大雜燴,可以說每個人都是外系生。我現在常常會挑以前沒涉獵過的東西,然後有時候一邊吃午餐,一邊亂看一課,當成小品影片長知識。

純粹的學習:在學校,你的成績跟別人的比起來雖然不是零和遊戲,但永遠有著相對的競爭關係,而在 Cousera 上,所有人都變成了盟友,學習是自己的事,跟別人無關,就算互評作業,給別人低分,也不會讓你顯得比較高,而且我發現外國人都超級 nice 的!因為這些關係,一切就變成了很純粹的學習,而且你會樂於分享。

聚焦在學到什麼,而不是分數:大部分的課程 Quiz 都可以做很多次,一開始的時候,如果沒有全部答對,我就會想重作,一直作到全對,慢慢的就會發現這樣很無聊,知道自己是不是懂,是不是有學到東西來的重要許多,我好像可以超脫在分數之外,在學校,你的能力會跟分數有若干的掛勾,即使你知道你會,有時還是要刻意的拉高分數,因為這是別人評斷你會不會的方法之一。

貢獻:因為我之前修 Stanford 的 Algorithms,新一期要開課的時候,就問我想不想當 community TA,有興趣的話可以填表單申請,不過那個問卷超麻煩的,問說:你為什麼想當 TA?你當 TA 的目標是什麼?你覺得你可以帶給學生或課程什麼幫助?你會怎麼做?你會投入多少時間,問題超級多,大概十幾個,每個都像申論題。我花了好一陣子思考要不要投入時間進去,好不容易下定決心,寫好問卷,結果還沒被選上。你看,一般在學校吃力不討好的 TA,在這邊大家搶著當呢!

其實我覺得 MOOC 離他的目標,高等教育的普及還很遠,我的感覺是,在 Coursera 上上越多課的人,通常都是本身已受過許多教育的人,因為上課學習也是一種需要培養的技能,這是一種習慣的力量。

最後在這篇推薦給想要嘗試 Coursera 的人,官方有 ipad 的 app,可以事先下載影片離線使用,讓你達到真正的「枕上、馬上、廁上」,手機上的我沒用過,覺得畫面會太小。另外也有第三方的 app -- coursistant,他有整合 coursera 跟 udacity,一開始我覺得 coursistant 做的比較好,不過官方 app 可能資源較多,開發活躍,慢慢就追上了,兩個都可以試試看。再來,為了避免「獨學而無友」,facebook 上也有華人的社團「Coursera/MOOCs 同學會」,可以進去看看。

大家一起努力吧!Go!

2013-10-31

工作中用到的 git 指令

來分享一下最近工作上常用的 git 指令,以及相關工作流程。

首先介紹一下工作的場景,在怎樣的情境下,怎樣的需求,讓我產生了這樣的 git 工作方法。我們主要的 VCS 其實是 mercurial (hg),但是有一個 git 的 mirror,所以可以在 git 的環境下改 code,但是最後必須到 hg 那邊把 patch push 上去,因此比較特別的是,我不會用到 git push,除非要 push 到自己維護的 repository,然後我必須要生成 hg 可讀的 patch 格式。

對應工具


editor: vim
difftool, mergetool: p4merge

alias


可以參考我的 .gitconfig,因為 programmer 的懶人特性,alias 會隨著時間越加越多
[alias]
  lo = log --oneline
  br = branch
  cbr = rev-parse --abbrev-ref HEAD
  df = diff
  dt = difftool
  mt = mergetool
  st = status
  co = checkout
  ci = commit
  cp = cherry-pick
  showt = "!f() { git dt $1^ $1; }; f"
  files = "!f() { git diff --name-status $1^ $1; }; f"
說明一下,絕大多數的 command 都縮寫為兩個字母,其中:
  • git lo -- 因為 git log 顯示的資訊太多,比較習慣一行一個 commit,所以設計了 lo,少個 'g' 表示比較短 XD。
  • git cbr -- current br 單純顯示目前 branch 名稱,主要是拿來給其他 script call 的。
  • git showt -- git show 可以顯示一個 commit 的 change 內容,加上 't' 表示要用 diff tool 顯示。
  • git files -- 列出某個 commit 修改了哪些檔案。

自訂 git 指令


可以在系統中放入 git-xxx 等以 'git-' 為 prefix 的指令 (放在 PATH 指得到的地方),然後之後就可以用 git xxx 使用那些自訂指令,打的時候會有 tab complete,所以當一個功能用 alias 做很複雜的時候,就可以把他獨立成一個檔案,結果就是一包各式各樣的 git-xxx,這包是從前人那裡 fork 來的,有很多功能,不過我沒有全部用到,fork 之後也立馬加了自己的東西,之後會提到。

開始修改


因為 git 的 light weight branching  特性,一般都是一個功能 (bug) 拉一個 branch 出來弄,所以在開始之前,我會先 sync 最新的 code 然後拉一個 branch 出來。另外為了避免弄髒,我會保持 master 跟 origin 同步,不會直接在上面操作自己的東西。
git co master
git pull
git co -b <branch>
開一條有最新 code 的 <branch> 出來。

改 code


就用喜歡的 editor 改 code,測試,這邊沒什麼。

檢查修改


當修改差不多到一個段落後,改動越小越好,但看起來必須有相關起且完整的。這時候我會開始 commit,但是真正 commit 前會先看一下改了哪些檔案
git st
輸出大概像這樣
# On branch demo
# Changes not staged for commit:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#       modified:   dom/icc/tests/marionette/test_icc_card_state.js
#       modified:   dom/icc/tests/marionette/test_icc_info.js
#       modified:   dom/system/gonk/RadioInterfaceLayer.js
#       modified:   dom/telephony/test/marionette/test_outgoing_emergency_in_airplane_mode.js
#       modified:   dom/telephony/test/marionette/test_outgoing_radio_off.js
#
# Untracked files:
#   (use "git add ..." to include in what will be committed)
留意哪些檔案是有修改的,以及下面 untracked 的地方有沒有新增的檔案,有的話記得 commit 時要特別加進去。

當發現某個檔案出乎預期被動到了,會進去仔細看看改了什麼
git df <filename>
覺得太複雜就用 diff tool 開
git dt <filename>
如果發現是不小心動到,或是一些 debug/TODO 資訊,覺得可以拿掉了,就用
git co -- <filename>
那個檔案就會被 reset

commit


一般來說整個改動可能很大,會想把他拆成若干個 commits。我們先看最簡單的情形,打算把全部的改動放到一個 commit。
git ci -am "<commit message>"
複雜一點的,照 file 分割。
git add <file1> <file2> ... <fileN>
git ci -m "<commit message>"
注意 -a 是拿掉的,有時後不小心打了,就會全部連沒有 add 的也一起進去,解決辦法是參考後面會提到的「如何把一個 commit 拆成多個」。

最麻煩的,想將一個 file 內的改動,分散在多個 commits,這時候就要用 patch mode。
git add -p [<filename>]
有 file 的話就會只看那個檔案,不然的話就會全部一起看。執行結果如下
diff --git a/dom/icc/tests/marionette/test_icc_card_state.js b/dom/icc/tests/marionette/test_icc_card_state.js
index 535ff77..6f213b9 100644
--- a/dom/icc/tests/marionette/test_icc_card_state.js
+++ b/dom/icc/tests/marionette/test_icc_card_state.js
@@ -4,13 +4,15 @@
 MARIONETTE_TIMEOUT = 30000;

 SpecialPowers.addPermission("mobileconnection", true, document);
-SpecialPowers.addPermission("settings-write", true, document);

 // Permission changes can't change existing Navigator.prototype
 // objects, so grab our objects from a new Navigator
 let ifr = document.createElement("iframe");
+let connection;
 let icc;
 ifr.onload = function() {
+  connection = ifr.contentWindow.navigator.mozMobileConnection;
+  ok(connection);
   icc = ifr.contentWindow.navigator.mozIccManager;

   ok(icc instanceof ifr.contentWindow.MozIccManager,
Stage this hunk [y,n,q,a,d,/,j,J,g,s,e,?]?
他會一個一個 hunk (小改動) 問,互動式的選擇要不要 include 這個 hunk,打 '?' 可以看各個選項的意義,我常用的有:
  • y: 要
  • n: 不要
  • q: 之後都不要了,離開
  • a: 之後的都要
  • s: 如果覺得這個 hunk 太大,可以選這個,讓 git 把他分割的更小,再來問
  • e: 要,但是用 editor 開起來,可以作細部的 diff 格式修改
做完之後,就等於做完 add,可以 commit 了。
git ci -m "<commit message>"
接著 repeat,用 git st 看一下還有哪些 change,一直作到所有都 commit 進去。

commit 的合併與整理


在這之後可能還會繼續改 code、commit,會需要整理 commit,可以用 interactive rebase。
git rebase -i <commit hash>
使用後會用 editor 開起來,上面每行代表一個 commit,下面的是說明。
pick d0c994e Add setRadioEnabled API (idl)
s daac55b Add setRadioEnabled API (dom)
pick a10139d Add radiostatechange in listener (bluetooth)
r d6b0ee2 Add setRadioEnabled API (ril)
pick f18042f Add setRadioEnabled marionette test

# Rebase f17c1b3..15b27cb onto f17c1b3
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out        
一開始前面的都是 pick,我常用的有:
  • 調換 commit 的順序 (之後可能會 merge fail),
  • s: 合併到前一個 commit
  • r: 改 commit message
  • e: 在打完這個 commit 後停下來,可以做些事
  • 刪掉那行,這個 commit 就會不見 (有可能讓後面的 merge fail)
存檔關閉,rebase 就開始跑了,如果都沒事,就結束了,有 conflict 的話他會說,遇到了直接輸入
git mt
會一個一個檔案用 merge tool 開起來,three-way merge,修改完存檔就表示那個檔案 resolved 了,全部做完後
git rebase --continue
繼續 rebase,有時候 (很少) 修完 conflict 後會發現這個 change 變成空的,這時候會打不進去,可以用
git rebase --skip
跳過這個 commit,通常這在 sync 新 code 要 rebase 才會出現,例如你要改的地方已經被別人先進了。

有時候 conflict 改一改,發現弄爛了,可以砍掉重來,取消這個 rebase,就會回到 rebase 之前的狀況。
git rebase --abort

commit 的分割


有時後會需要把一個 commit 拆成多個,假設要修改的 commit 是最後一個
git reset HEAD^
可以保持檔案內容不動,但是讓 commit history 回到上一步,這時候再用之前的 git add、git add -p 之類的弄出想要的 commits。

如果要拆開的不是最後一個呢?
git rebase -i <commit_hash>
用 rebase 回到前面一點的位置,在 interactive 時把你要拆開的 commit 標成 e (edit),這時候 rebase 到那邊就會停下來,現在你要修改的 commit 就是最後一個了,套用上面教的方法,最後做完後,記得要讓 rebase 繼續跑下去 git rebase --continue。

[自訂] git qapplied


通常我會打 git qa<tab>

這是 git log 的變形,一般來說我們只關心自己加的那幾個 commit,過去的那些 history 不太需要看,這個指令會從你的 branch 與 upstream (master) 的 LCA (Lowest Common Ancestor) 開始列出 log,
Branch demo (downstream from origin/master)
f17c1b3 .. enable ril_debug
d0c994e Add setRadioEnabled API (idl)
daac55b Add setRadioEnabled API (dom)
a10139d Add radiostatechange in listener (bluetooth)
d6b0ee2 Add setRadioEnabled API (ril)
f18042f Add setRadioEnabled marionette test
有了這個之後,我就很少用 git log 了

[自訂] git qrebase


通常我會打 git qre<tab>

類似 qapplied 的概念,在 interactive rebase 時,git rebase -i <commit hash> 都要打一個 commit hash,其實這個值也可以用 LCA 帶入,因為反正會修改的都是自己的那些 commits。

Sync 到最新的 code


隨著在自己 branch 上的開發,master 也會一直往前進,在 deliver patch 前要確保自己有 sync 到最新的 code,這樣產生的 patch 才是基於目前最新的 code,別人打你的 patch 時才不會遇到 conflict。
git co master
git pull
git co <branch>
git rebase master
如果在 rebase 中遇到 conflict,就用前面提到的方法解決。

Patch 的產生


上述的那些方法,可以做出乾淨整齊的 commit,再來就是把這些 commit 輸出成 hg 格式的 patch 檔,可以給別人 review 或是到 hg 那邊 push。

我有改一個類似 git format-patch 的 git format-hg-patch,他會先用 git format-patch 產生想要區間的 patch 檔 (通常我是下 -N, 表示倒數 N 個 commit),接著自動用另一個 tool 把 git format patch 改成 hg format patch,如下:
aknow@aknow-pc:~/workspace/mcg/patch$ git qapplied
Branch demo (downstream from origin/master)
f17c1b3 .. enable ril_debug
d0c994e Add setRadioEnabled API (idl)
daac55b Add setRadioEnabled API (dom)
a10139d Add radiostatechange in listener (bluetooth)
d6b0ee2 Add setRadioEnabled API (ril)
f18042f Add setRadioEnabled marionette test
7497290 Modify related tests
a7bf0a9 .. remove enable radio when power on (ril)

aknow@aknow-pc:~/workspace/mcg/patch$ git format-hg-patch f17c1b3..7497290
0001-Add-setRadioEnabled-API-idl.patch
0002-Add-setRadioEnabled-API-dom.patch
0003-Add-radiostatechange-in-listener-bluetooth.patch
0004-Add-setRadioEnabled-API-ril.patch
0005-Add-setRadioEnabled-marionette-test.patch
0006-Modify-related-tests.patch

aknow@aknow-pc:~/workspace/mcg/patch$ showpatch
HG Part 1: Add setRadioEnabled API (idl)
HG Part 2: Add setRadioEnabled API (dom)
HG Part 3: Add radiostatechange in listener (bluetooth)
HG Part 4: Add setRadioEnabled API (ril)
HG Part 5: Add setRadioEnabled marionette test
HG Part 6: Modify related tests
showpatch 是我另一個小工具,會看目錄下的 .patch,然後列出他們的 title。

再來因為我們的 commit mesasge 中要加上 bug id,我覺得如果每次 commit 都要在 commit message 中打實在很麻煩,尤其是那個 id 一般來說都不會記得,要另外去查, 造成頭腦的 context switch,所以我選擇弄另一個 tool 出來,markbug,負責幫 patch 檔加上想要的 bug id。

至於要怎麼查 bug id 呢,我加了一個指令 git bugs,使用的話要先裝兩個 python packages:
  • bztools: 查 bugzilla 用的 api
  • colorama: 因為我很假掰的想要讓 console 的輸出有顏色,但是不想打色碼
所以一整套用下來大概是這樣:

  • git bugs: 查 bug,複製對應的 bug id
  • git qapplied: 看要生成幾個 patch 檔
  • git format-hg-patch -N | markbug <bug id>:  經過一個 markbug 的 pipe,貼上 bug id
  • showpatch: 檢查最後結果
showpatch 現在前面的綠色 HG 感覺有點雞肋,那是以前還沒弄 git format-hg-patch 時,hg patch 的生成是拆成兩步驟的,有時後會忘記 convert,所以特地在這邊檢查 patch 的格式是 git 還是 hg。

結束,其實用久了,都是那幾個而已。

2013-09-20

選擇錢還是熱情?

可能是年紀到了,最近腦中總是有一堆人生思考,如果不偷懶的話,大概可以寫成好幾篇吧。 

這幾天在 PTT Tech_Job 版看到有人在問「錢重要還是熱情重要?」,基本上這是個月經文,也會以不同的問法出現,例如「要選擇錢多還是有興趣的工作?」。看了看討論串,覺得都沒有很點到我心中的想法,所以我決定上來喇賽一下。

首先摘要一下大部分人的觀點

  1. 「有錢就可以激發熱情」 => 錢
  2. 「做很有興趣的工作最後也會被鳥事磨光熱情的」 => 錢
  3. 有熱情才能堅持賺到錢。最終是錢」 => 錢
  4. 「應該是問興趣熱情值多少機會成本」 => 錢

我不是故意只節錄選擇錢的,而是大部分選擇熱情的都比較像是 3,或是像是成功人士愛說的「不是為了賺錢而出發,而是當你把事情做好,錢自然就會進來」。

看到這裡,你可能會覺得我會說應該要選擇興趣,是的,如果你問我這個問題,我的答案會是「熱情」,但是每個選擇的背後,都跟那個人的立場有關,所以這個答案也只適合當下的我,並非每個人都該這樣選擇,可以參考支持 passion 的論點:The 10 Reasons You Should Follow Your Passion And Not The Money

首先,這個問題是個不公平的比較。

Ref: http://tds.ic.polyu.edu.hk/td/theme1/image/maslow.jpg

根據馬斯洛金字塔,人必須先滿足生理需求、安全需求,而這些基本的食衣住行都需要錢,所以在這個階段錢非常的重要,但是熱情就比較沒有關係了。

接著到了上面的尊重與自我實現,這就比較像是熱情的部份,今天因為我已經有滿足生理需求的錢,也認為熱情可以幫助我達成自我實現,所以我的答案就是熱情。但是也有人會覺得錢可以為你帶來尊重,帶來自我實現,那麼對他來說錢就是他的目的。

你的人生要什麼


如果再往上看,其實錢跟熱情都不是你人生真正想要追求的東西,他們只是可以幫助你得到你想要的中繼品。對於這點,我比較認同在 << 更快樂 >> 一書中所說的「快樂,是人人渴望的終極貨幣」,人要的其實很簡單就是快樂的一生,每個人對於錢可以帶他多少快樂,會有各自不同的比例,而對於追求熱情也是。所以我們要換算的不是,這些熱情等價於多少錢,然後拿來跟錢多的比,而是應該通通換算成快樂的指數。

看到有一個人這樣推「不管熱情或錢 能讓你快樂過日子比較重要 自己取捨吧」,大概有點接近我心中的想法了。

我要的是什麼


於是慢慢地就會開始想,我這一生想當個怎樣的人,最近學到一個 CENTER 的概念,CENTER 是 Character、Entrepreneurship、owNership、Tenacity、Excellence、Relationship 的縮寫,藉由回答一些問題,可以更瞭解自己,知道自己想成為怎樣的人,以及該怎麼做。

我並不喜歡賺錢,我可能會說自己沒錢,錢很少,但是讓我選擇要不要把時間花在賺錢上,其實我也不想。這從一些人創業的動機就可以看出來了,有的出發是賺錢,市場評估後,覺得會賺就進去了。例如說,台灣的飲料商機非常的大,透過簡單的估計與計算,要知道在某個地方開一家店(也許是加盟)會不會賺似乎沒有很難,如果我是一個愛賺錢的人,當發現會賺的時候,就會意無反顧的進去,而且這還是打造被動收入的現金流,多好阿。那我會不會想做呢,想了想覺得還是不會,這件事對我來說就是單純的賺錢,一點都不有趣,除了可以當店長,聘請年輕美眉來打工。所以如果哪天我真的開了一家飲料店,我應該只是嘴饞自己想喝,或者是想要提供就業機會給... XD。

我喜歡寫程式,但是更進一步思考,我喜歡的應該是動腦解問題,喜歡擁抱技術,喜歡變強的感覺,就像角色扮演遊戲練功一樣。最近我每一天睡前都會問自己,今天是不是有學到新東西,今天有沒有比昨天的自己更厲害。有一個說法是「只要你每天都進步 1%,一年下來就會進步到 37 倍。」,說實在的我覺得這是屁,人的成長怎麼可能是指數型的,但是不可否認,我們依然可以選擇讓自己每天都有所成長。

有篇文章在說,喜歡寫 code 的人都不賺錢,其實沒什麼,就是選擇而已,每個人獲得快樂的來源不同,追求的東西也會不同。

所以讓我草草結論一下,認識自己真的很重要!!!