My Travel in Programming

hh


  • Home

  • About

  • Archives

離職休息、閱讀學習、記錄

Posted on 2018-05-03 |

最近我提離職了,也休息了幾個禮拜了,這期間我在回想著我過去的經歷,也把之前的線上課程上一上(雖然還有一堆…),所以也才有了時間可以寫部落格 :)

從出社會第一家做比特幣交易所的公司

一開始因為很開心在104滿滿的職缺海中找到了可以用python的職缺!後來有著各種原因讓我決定離職,其中像是,

  • 原本有位要來的前輩沒來(後來跟我點出公司一些問題後,去了痞客邦當machine learning tech lead)
  • 跟我一樣是新鮮人的夥伴離職
  • 公司決策不停變換(畫大餅有點不實際)

所以我在想我是不是應該換個地方,因此也才在下一間遇到了一些不錯的夥伴,但是…

第二間是個做交友網站的公司

這間一開始吸引我的點是,他用了django這個framework來開發,剛好是我想要的,雖然一開始我進去只有我、技術長、跟一位mis兼程式開發的前輩,三位RD,加上一位美術和櫃檯,有點辛苦的撐著XD 但是後來慢慢的徵了

  • 前端
  • 後端
  • android工程師
  • 美術

公司有慢慢的壯大,其實我滿喜歡這種感覺的,和夥伴一同努力! 但是我只能說新創…就是變數大,期間發生了很多事情,公司商業變換政策往大陸發展,開始變控股公司…甚至董事長詐騙募資人的錢,捲款落跑,公司最後是倒閉收場.. 老實講若沒有發生這些很扯的事情,我想我會繼續待在這家公司,因為有著不錯的夥伴!

接著第三家公司博奕公司

其實這家是hr主動找過來的,原本的我出於好奇心,因為對於博弈產業不了解,所以來面試看看這家,結果意外的感覺其實沒我想的那麼不好?! 不知道是不是因為在松菸誠品的關係所以印象加分不少XDD 但是由於公司是做技術提供,所以我就把它想成跟一般外面軟體公司類型很像,只是產業別跟客戶不同。

不得不說博弈背後真的就是錢的實力雄厚,光是能夠租在松菸誠品然後,在我進去公司九個月,十個月的時間後,公司從2x人成長到5x人就是個證明。

在這邊也遇到一些不錯的人,但是為何最近我離職了呢? 原因大概是下面幾點。

  • 東西學習了差不多
  • 不少夥伴走人了
  • 陷入了部門鬥爭…
  • 沒有往上發展的機會

當然還有著其他因素,詳細的內容這裡就不多說明,免得…咳咳,據說我離開之後情況還是沒啥變化,看來我給予的建議,應該是被當作耳邊風吧,這間我離開的時候,老實講沒有上一家那麼依依不捨,我想因為是鬥爭的感覺吧…

仔細想想,我果然還是很在意相處的感覺更甚於錢,也不是說我care錢拉,錢很重要! 尤其是對於社會的現實,沒有錢的你,又能夠做到什麼? 但是我覺得即使身在社會的洪流裡,還是得保持著妳的初衷,不要因為外面而過度讓你自己變得不像自己。

休息的這期間,我思考著我未來的展望是什麼? 其實該怎麼說呢,對我來講就是兩個字,學習,老實講我並沒有像一些人很明確的,直接說未來要當技術長、軟體架構師或是資料分析師什麼的,我只是純粹認為programming很有趣,把想法現實化,很有挑戰但也很有成就! 但是對於想要的公司類型呢? 的確我現在似乎有點迷惘,因為對我來說主要是和人相處的感覺吧,但是這又不是面試可以面出來的… 哈哈,所以最近在迷惘中閱讀與記錄!

該來講講這次要記錄的東西了!! 之後還會發個幾篇心得文,畢竟最近比較閒! 哈哈,今天要講的其實也沒什麼特別深度,是要講emacs lisp相關的東西。

Emacs lisp 初學心得

最近會想看emacs lisp,其實是有點原因的,老實講emacs一直以來我都沒有認真去學習他,其實最大原因就是懶(嘿嘿),因為使用他前實在是需要很多客製化動作,就算是使用spacemacs老實講,我覺得還是需要一些客製化,雖然它已經讓emacs的上手度大大降低很多了,只是用了一陣子後,我總覺得spacemacs實在有點肥,有太多東西是我用不到的,有時候我想要的功能她沒有,而且老實說對於程式開發,像是python的應用開發,我已經從spacemacs轉去vscode,vscode對於不少語言的套件品質其實都滿高的,而且debug方面的功能也都做得不錯,話雖如此,emacs也有著其他人無法取代的套件,例如:magit,所以最近我在思考著我是否該先好好的了解emacs本身,竟然有人可以開發出這麼好用的套件,同時也在幻想著,我之後可以客製化emacs將它變成我得心應手的工具,所以我開始嘗試著學習一下emacs lisp這個語言,emacs本身就是用這個語言寫成的。其實也是滿有趣的

emacs lisp -> emacs -> ?

emacs lisp 生出 emacs ,emacs又可以讀取emacs lisp,簡言之我可以藉由emacs lisp將emacs變成其他東西,不一定是一個程式編輯器。好了,以下開始記錄我一些關於emacs lisp的小小心得。

emacs hello world

首先我們打開emacs到下面截圖中scratch這個buffer來,這是可以讓你隨意玩emacs lisp的地方

scratch

接著來段hello world吧,

1
(message "hello world")

接著按下C-x C-e記得要把游標移到行尾,因為他執行的功能是,evaluate the last s-expression,s-expression指的就是lisp相關方言的表達式,有興趣可以查看看wiki,這篇不會特別著墨lisp相關知識,但是這邊還是稍微提一下,sexp是一種標記法,表達巢狀list,一般都是只有儲存data,但是lisp卻連程式碼都存,這也是為什麼接下來後面程式碼你都只會看到一堆括號的原因~

hello world

轉回正題,上面執行完後你會看到最下面有印出hello world的字樣,那就是你的執行結果。另外其實你也可以到Message的buffer看,那裡會有所有訊息包括歷史記錄,接著來稍微講一下emacs lips的primitive data跟一些性質

由於前面提到的sexp,你會發現lisp是一個前序表達的方式,來看看(+ 1 3) 這個其實換成中序表達,會是(1 + 3),沒錯!這個表達式的答案就是4,lisp在背後真正的表達應該是長這樣的(+ . (1 . (3 . nil))),.這個符號就當作list串接的意味吧 :)

關於lisp語言本身,我想之後我再來寫一篇記錄好了,轉回來,經由上面的那個表達式,其實應該也不難猜出,其實我們是可以做這樣的事情的 (+ 1 2 3 4 5) 也就說我們可以用任意參數,不信的話,你是可以試試看,另外若是沒寫過python, 可能會對於上面的加號其實是一個function感到訝異吧~ 別懷疑他就是函式,在emacs裡,你可以把游標移到你想要的函式上面,然後按下C-h f 你就會看到相關的文檔敘述跑出來。

再來對於elisp,他其實沒有boolean type,只要是nil,或是empty list都是屬於false,其餘都是true,關於變數呢~ (setq a 3) 這樣就會將a變成3,但是這是全域的,若是要用區域變數的話

1
2
3
4
5
(let (a b)
(setq a 3)
(setq b 2)
(+ a b)
)

像上面,a和b只會在let區塊裡面有作用,如果你在外面使用a or b會發現他們並沒有被定義。

再來progn 一個很特別的語法,可以把它當成一個區塊的expression,然後他會回傳最後一個expression

1
2

(progn (setq b "5") (message b))

上面progn本身並沒自己形成一個區塊,因此b是個全域變數。

再來就是關於迴圈方面elisp有提供while

1
2
3
4
(let ((x 32))
(while (< x 127)
(insert-char x)
(setq x (+ x 1)))

上面這段會執行在游標插入一些字母和符號~

elisp function定義的方式,defun name (pram..) 如下

1
2
3
4
5

(defun sayHi ()
"this is a doc string" ;; 這是function doc
(interactive)
(message "hi"))

上面這樣就是定義了一個函式會印出hi的字樣,其中特別的一點是,(interactive)會讓這個function變得可以從emacs中的M-x呼叫,不加的話,你就不會看到sayHi再M-x列出的函式清單。

整體上而言想要學會怎麼客製化emacs的話,對於lisp方言的掌握度,並不需要很深,只要懂的一些基礎就夠了,頂多只差在效能,接下來嘗試做個小小的功能,比如: 在轉寫程式碼時,有時候我忘記把單字放在””裡面,所以我想要有個功能是能夠快速把游標上的單字左右加上”這個符號。

1
2
3
4
5
6
7
8
9

(defun quotify-region (start end)
"insert \' or \" at the beginning and end of your
selection"
(interactive "r")
(save-excursion
(save-restriction
(goto-char start) (insert "\"")
(goto-char (+ end 1)) (insert "\""))))

上面這段就是簡單版的實現,你只要選取你想要的單字後,呼叫這個函式就會看到效果了,比如接下來下面的demo,你會看到我選取的evil,然後將" "加在他的前後!

demo

其實上面這段程式碼,是我還在亂玩emacs的設定,新手上路,請不要電我~ 這邊呢,我有將我剛剛實作的功能綁定在evil的visual mode的快捷m然後按' 就會看到神奇的效果了。

To Sum up

現在充斥著各種editor或是ide,其實認真講,我真的要開發golang, python, nodejs, 之類的我還是會用vscode,emacs目前我個人是當作興趣玩玩,順便來看看他可以在什麼地方用上,增加我的開發效率,然後經過稍微碰觸emacs lisp後,我只能說他真的太多東西要讀,我當前還沒整理出一個開發套件的模式,比如要去哪找你想要的函示名稱,這種東西一定需要一個地方可以讓你快速參考,還有就是對於lisp其實當前我只有基礎而已。

但是我深刻體驗到emacs的彈性了,真的是很容易就實作一個功能馬上應用,我想我會繼續挖掘emacs的特點,繼續記錄一些心得,下面reference的部分是一些連結我覺得可以看一下的。

Reference

  • elisp cookbook
  • elisp simple tutorial
  • official elisp manual
  • learning emacs
  • ergoemacs lisp manual

下面兩個是關於lisp的免費書,想要更深入了解的人,建議可以去看一下

  • ansi common lisp
  • practical common lisp

dynamic array

Posted on 2017-04-24 | In python |

緣由

最近正在上一堂線上課程python for data structure algorithm and interviews,其中遇到有趣的點,想說特別來記錄一下,用python這麼久,也沒特別注意這個問題,今天要講的東西就是python list,老實講我並沒有看過源碼是怎麼實作的,原本我都以為python list是個類似link list的實作咧 :P 誰叫他名字有list 哈哈,但是python的list,實際上就是類似c++的vector那種動態array的機制,一開始就先給你一定容量的container,等到滿了就在new一個容量更大的array,然後搬過去,可以看看下面的測試例子就會知道了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import sys


lst = []

for i in range(10):

print('{} {}'.format(len(lst), sys.getsizeof(lst)))
lst.append(i)

# output
#0 64
#1 96
#2 96
#3 96
#4 96
#5 128
#6 128
#7 128
#8 128
#9 192

可以看出size會等於某數的倍數成長,我們可以來做點簡單的方程式來實際看看到底是哪個數字

$$
x + y = 64 \\
x + 2y = 96
$$

輕易的可以知道那個數字是32,至於一開始的32,是list這個物件其餘的size,python的 sys.getsizeof並不會像c或是c++得到該資料型態的size,原因就是python所有資料型態都是物件,還要加上gargabe collection!,若是想要使用C或C++那種sizeof,就必須使用python ctypes的sizeof。

另外有趣的是我們可以藉由python ctypes來實作一個小小的dynamic array。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import ctypes

class DynamicArray:

def __init__(self):
self.n = 0
self.capacity = 1
self.A = self.make_array(self.capacity)

def __len__(self):
return self.n

def __sizeof__(self):
return ctypes.sizeof(self.A)

def __getitem(self, index):
if not 0 < index <= self.n:
raise IndexError('out of bound')

return self.A[index]

def append(self, ele):
if self.n == self.capacity:
self._resize(self.capacity*2)

self.A[self.n] = ele
self.n += 1

def _resize(self, new_cap):
B = self.make_array(new_cap)

for index, ele in enumerate(self.A):
B[index] = ele

self.A = B
self.capacity = new_cap

def make_array(self, new_cap):
return (ctypes.py_object*new_cap)()

以下我們來測試一下,進行跟python list類似的範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

from ctypes import sizeof

darry = DynamicArray()

for i in range(20):
print('{} {} {}'.format(len(darry), sys.getsizeof(darry), sizeof(darry.A)))

darry.append(i)

#output
#0 32 8
#1 32 8
#2 40 16
#3 56 32
#4 56 32
#5 88 64
#6 88 64
#7 88 64
#8 88 64
#9 152 128
#10 152 128
#11 152 128
#12 152 128
#13 152 128
#14 152 128
#15 152 128
#16 152 128
#17 280 256
#18 280 256
#19 280 256

前面提過sys getsizeof這個方法,官方文檔,可以看出他是實際上只針對該物件內容進行size計算,而不會另外去針對裡面內容額外指到的物件,因此這裡的獲得的size不會因為我們new array而有所改變,甚至是class多增加屬性也不會增加其size,所以~ 這裡我才改寫了__sizeof__讓他比較像原生lst的行為。

Amortized Analysis

那麼對於這種動態array,他的append實際上效率要怎麼計算呢? 因為你會發現並不是每次append都需要resize對吧,所以我們必須想個方法來衡量這個效率,這時候就要來講講amortized analysis了, amortization中文意思是分期償還,從這個意思來看,大概有點FU了吧,接下來要對dynamic array的append效率來做分析,首先就先來觀察並分析如下表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def append(self, ele):
if self.n == self.capacity:
self._resize(self.capacity*2)

self.A[self.n] = ele
self.n += 1


def _resize(self, new_cap):
B = self.make_array(new_cap)

for index, ele in enumerate(self.A):
B[index] = ele

self.A = B
self.capacity = new_cap
No. ary cap cost is resized
1 1 1 x
2 2 2 o
3 4 3 o
4 4 1 x
5 8 5 o
6 8 1 x
7 8 1 x
8 8 1 x
9 8 9 o

每一次要resize時都會做copy動作,看看No. 9 那裡較明顯,因為要插入時發現是滿的,所以就做了resize,裡面動作有做到8次assgin(將前面的array內容複製到新array裡),另外一次就是新資料的輸入,所以有需要resized的情況下第n次append裡面就會做了n次assign動作,下面開始列出數學函示做點分析

$$ \frac{1+2+3+1+5+1+1+1+9+…}{n} $$

2, 3, 5 , 9 可以變成 (1+1) (1+2) (1+4) (1+8)

$$ \frac{(1+1+1….+1) + (1+2+4+8+..)}{n} $$

可以再繼續變化成下面這樣

$$ \frac{ \sum_{i=1}^n 1 + \sum_{i=1}^{(\log_2{n-1}) + 1} 2^{i-1} } {n} $$

題外話

1
2
3
$$ \frac{ \sum\_{i=1}^n 1 + \sum\_{i=1}^{(\log_2{n-1}) + 1} 2^{i-1} } {n} $$
# 根據你使用的markdown渲染引擎不同,你可能會遇到一些小問題,上面這行就是上面那個數學公式,
# 我一定要在 _ 前面使用escape symbol (\),才能正常表現,但是我在ipython就不需要~

等比級數公式

$$ S_n = \frac{a_1 - r^n}{1-r} $$

藉由等比級數可以將上面式子變成

$$ \frac { n + 2^{(\log_2 n-1) + 1} } {n} = \frac { n+ 2*(n-1)} {n} = \frac {3n-2} {n} $$

算完之後這個效率等於 $ O(1) $

amortized analysis相關知識

有三種方式

  • the aggregate method
  • the banker’s method
  • the physicist’s method(potential function)

The amortized cost per operation for a sequence of n operations is the total
cost of the operations divided by n.

其實說穿了amortized cost就是一種平均成本的概念,n次操作中,僅少數是昂貴的操作,絕大多數都是便宜的操作,平均後就會是便宜的操作,上面dynamic array就是個例子

A potential function is a function of the state of a system, that generally should
be non-negative and start at 0, and is used to smooth out analysis of some algorithm or process.

老實講我還沒弄很懂這麼多種的分析方法是…但是對於我們上面這種分析方式,應該算是aggregate method,就是硬底子的去分析每一步操作去計算平均效率這樣,這堂課還有許多內容我還沒上完,之後有遇到有趣的東西的話,還會上來紀錄一下的。

github-page-auto-deployment-with-travis-ci

Posted on 2017-04-13 | In web |

緣由

其實個人有計劃未來要手動自己打造static website,跟撰寫部落格的工具,只是在此之前我想到說我之前的部落格是放在非static website的,如果一次轉換這麼大會經歷很痛的轉換期,再加上我想要先試用一下這類的工具,這樣我之後才知道什麼地方是我想要的,什麼是我不要的,所以我就找了一下順眼的工具來使用,這篇部落格會來講解使用hexo結合travis ci和github page,達到自動部署的靜態網頁的效果。

開始轉換之路

最一開始其實我是在google blogger上面寫部落格,後來碰到markdown語法,覺得簡單也滿喜歡的,所以我就一直找可以使用markdown來寫文章的平台,找著找著就遇到logdown,使用上挺簡單的,介面也很簡潔,之後就是一直用它了,是到最近想說想要親手打造一個static web site generator,因此才開始轉換我的部落格。

幸好logdown本身就有提供匯出你的文章功能,只是要注意的是它匯出的格式是Octopress,hexo真是剛剛好有支援這種格式,哈哈,其實也不是那麼剛好拉,除了順眼外,我還有故意挑是否有支援Octopress格式的,hexo第三方套件數量不少,能夠選擇的主題也很多,主題我是用hexo next theme,相關細節設定,就不多說明了,文檔算是挺清楚的,再來講講使用hexo可能會遇到的問題,一般都是出現在render時,也就是當你run hexo g or hexo s會遇到的,他的提示是render時遇到syntax error,直接跟你講幾乎問題都會出在你的文章們,裡面可能包含了什麼語法讓他parse錯誤~

github page

關於什麼是github page,可以到官網,簡單來說就是github提供可以讓你放置靜態網站的服務,有分為兩種

  • user or organization site
  • project site

比較需要注意的是,user or organization site,強制只能把網站相關的檔案放在master,你當然也可以故意放在其他branch拉,只是就會空白:p ,屬於user or organization site類型的,github是會去讀取branch master,屬於project site的話,就可以自由選擇要讓github去讀取哪個branch,這點要注意,也因此我的主要開發branch就是develop(另開的),寫完文章或是調整設定後,我就會run hexo g,產生靜態檔,之後就是把這些靜態檔放到branch master,所以你會發現我的master和develop檔案內容差異很大,可以到我的repo看看就會比較清楚我在講什麼了,到這邊為止其實整個部落格撰寫流程已經可以運作了

  1. 撰寫文章.md
  2. run hexo s 看看是否OK
  3. run hexo g 產生靜態檔
  4. git checkout master 將靜態檔,搬到外面來
  5. git push origin master 看狀況branch develop也可能會需要push

身為一個程式人,就是懶,當然能夠自動化的地方就讓電腦來,不是嗎? 或者是減少重複性質動作也好 :)

步驟1~3這邊無可避免必須是我們要自己來,畢竟電腦又不知道你寫的這文章是否是最終版本要上線了,但是步驟4跟5呢? 這邊跟你說! 是可以的!,也是這篇我主要想要跟大家分享的,我個人是這麼做的,步驟1~3後,接下來我就push branch develop,之後travis ci偵測到github的branch develop有所變動,接著就clone了我的專案,進行產生靜態檔,把靜態檔弄到branch master,之後在push,簡單說就是步驟4~5都被travis ci給拿去做了,那麽我們就先來看看travis ci。

travis ci

travis ci是一個持續整合的平台,一般而言都是拿來當作run tests,比如,當有人發出pull request到你的repo,你可以讓他先run過測試檢查看看是不是符合最基本需求,至少他沒有把你的功能做壞,如果通過的話,你可以考量是否採納這個pull request,更進一步的人,會在測試成功後,做deploy的動作,也就是部署,所以github page是可以利用這個來達到自動部署的!

至於要怎麼和github page做結合呢? 有想法是一回事,實際做又是一回事,實作中一定會遇到問題,其實有點sense的人,應該一開始就會想到他是怎麼git push的,不是都要輸入密碼嗎? 關於這個首先就要來看看github personal access token,藉由這種方式就可以避免掉需要輸入username和password了,像這樣git clone https://<token>@github.com/owner/repo.git,至於要怎麼建立access token呢? 到你個人的github settings頁面,左邊側邊欄會有personal access token,進入後就會看到類似下面的頁面

access token page

再來就是建立token,詳細的就不多說了,接下來就要把你的token給放到travis ci相對的project的環境變數,每個自動整合的project,都可以點進去做設定,它提供了可以設定環境變數的功能,也就是說在你的shell裡可以使用你設定的變數,避免你的script直接透露token值! 如下面這張圖

travis ci image

最後就是撰寫自己的deployment的shell script了。

總結

hexo使用起來的確簡單,但是使用這個工具,還是需要有點程式知識的人才有辦法輕鬆學會,相對而言,logdown提供了一個不錯的撰寫環境,使用起來很簡單,我想之後有時間我開始實做我自己想要的撰寫部落格工具。

mithril 開發官網記錄

Posted on 2017-04-06 | In javascript , web |

短期官網開發記錄

最近公司有個臨時的官網需求,想要兩個禮拜內就要出爐,聽到這麼急的需求,當下我當然是有點傻眼,因為只有我一人做,然後重點還要等設計圖,做電腦版還有手機版,跨平台的版面有時候可是很難搞的…尤其是也不是說很簡單的設計,所以其實挺趕的,因此在設計圖出來前,就先大概想像的應用情況,以下來講一下我的考量。

library 規劃考量

這邊因為頁面一開始得知,設計想要做single page application,想當然我可以先決定一個主軸library來使用,這邊我是找個類似react的library來使用,為何我不用react呢? 是有幾個考量

  1. library 大小太大
  2. 跟es5的js library相性不好(也就是不好結合)
  3. 需要搭配bundle tool

基於種種考量,這裡我選擇了mithril js,至於理由很簡單你把上面理由全都相反過來就好,再加上這個官網性質其實並沒有大型或複雜到需要用很有系統的寫法,用react就有點大材小用,個人覺得除了故意要學習用途外,每一個library都有適合他的地方,不是誰誰誰就是棒,其他都爛爛爛,轉回正題,mithril js 其實api簡單,文檔也很不錯,基本上讀完文檔她的用法你就懂了,唯一要注意的就是life cycle的beforeremove method,這個後面的地雷區會提到!

再來這邊我想到了多語系問題,之前我都是用django的i18n,但是這次由於頁面是用js來渲染的,所以我把i18n相關的功能也讓前端負責,我用的套件是i18next,使用方法很多這邊就不多介紹了XD 很方便的是你可以直接把把內容寫在js裡,包在json格式就好,後來隨著設計慢慢有想法後,需求也漸漸明朗,考慮到部份動畫需求所以就用到了svg,這裡相關套件是snap svg,再來就是頁面滑動效果是整頁一頁一頁捲動,直接給你們看我是用什麼套件就知道我想要表達什麼,我用到的是fullpage js,這個library的start數會那麼高,其實也不是沒理由,真的寫得不錯,接口留得不錯,很好用,最後考量到根據平台不同(手機或電腦)我需要渲染不同的html組件,因此我需要一個偵測user agent的library,這邊我是用到mobile detect js,這邊總結一下我用到的主要library有哪些:

  • mithril js
  • i18next
  • snap svg
  • fullpage js
  • mobile detect js

從這邊可以發現有許多lib都是用es5寫法吧,mithril js他是可以用es5和es6寫法的,這也是我選擇他的主因,當然他的渲染效率也是頗高,或許有人會說他不需要用套件自己都可以寫,當然拉,全部任何東西都可以自己寫啊~ 包括react你也自己寫一個給我看看,在兩個禮拜內,從前面這邊看到重點了嗎? 期限!! 許多東西都是基於時間而有所考量和採取應變措施,可以跟你說實際上我寫官網的時間只有八天,就得上線囉,後面會講到時間分佈。

套件研究

關於套件的研究,其實大致上我都是先看文檔,與這個open source community活耀度,來決定是否使用它,裡面大概只有svg我是直接看網路上教學文較多的來選擇,因為svg我之前沒用過XD 只有看看文章的了解程度而已,所以想藉由教學來快速上手,剩餘我上面列的套件,其實都挺簡單的,建議各位可以簡單寫寫東西,看看library是否和各位客官的口味再決定是否使用喔。

實際開發

再來就講講我實際上開發時遇到的問題,也就是我事前沒想過遇到的事情,其實正常下都一定會發生意料之外的事情拉,所以建議預估時程最好留個幾天用來應付意外的,還有保留一些事後修修改改的時間囉,因為頁面做出來後,是有可能會被要求那可不可以怎樣怎樣的,下面就來講講主要遇到的地雷 :p

遇到地雷及瓶頸

第一要來講的就是svg! Oh my godness! 他讓我耗費最多時間,因為設計師用AI產生的svg檔,跟他用AI上所看到的,實際上有所出入,我這邊是不知道為啥他弄出來後,少了好多好多的漸層和透明效果,我花了一些時間研究,有線索提出可能是svg內容產出的時候沒有把 mask linear gradient 等等放入,def裡面,因此..我抱著一線希望手動把上千行的svg內容整理,呼呼~ 這邊實在是幸好 spacemacs 對於文字編輯的好用,讓我省了一些時間,但是我也花了一整天在整理… 整理完之後,的確部分效果有出來了! 但是還是有些效果沒有出現..於是乎,我自己手動加上那些效果,參數也是自己猜,才幾乎還原到85%的樣子,總結上,光是讓svg正常顯示我就花了兩天時間,開發時程有八天兩天就噴掉了。

關於mithril js的life cycle的onbeforeremove這個方法,一開始我卡了半天,他竟然意外的難以觸發,官方說法是Note that the onbeforeremove hook only fires on the element that loses its parentNode when an element gets detached from the DOM其實這句我有看沒懂XD,最後我看github issue有點說,建議你的元素一定要被包在array裏,然後…就真的成功了,天殺的超難觸發的,另外這套件網路上的教學其實可以不用看,因為都有點過時了,建議直接看文檔就好~

其實我在整理svg的時候就有考慮是否放棄改成用切圖,然後展現動畫效果,可是能夠做的還是比svg少很多,但是基於我想學點東西的心態,其實我還是努力的去弄XD 畢竟這也是我離職前被託付的工作,也希望動畫效果能夠好些,而且responsive效果也不賴,畢竟svg自己就會去縮放了,但是事情沒有到這邊就為止,之後我使用了svg snap這個套件,遇到了天殺的問題..

svg snap

一開始我有在教學網站試玩了snap,想說還算挺好用的,也大概玩出點心得,可是就當我開始去下載官方最新版snap 0.5.x ,開始開發專案,發現莫名的,我是動畫位置總會莫名偏移,這邊我就直說了,我花了半天..終於發現這是一個超大bug,像是你想要旋轉一個長方形360度,這樣簡單的功能就會爆掉了,他的中心點會無故地亂跑,我看到github上面已經有人發出issue,個人建議是如果你想用的話,就先使用0.4.x版的吧,這版你會用得比較快樂,但是由於遇到這些種種地雷,svg動畫方面我就先暫時擱置了,轉而先把版面先弄好再說,因為畢竟可是有電腦版和手機版,而且就算是電腦版也有不同螢幕尺寸問題,需要一定時間去調整。

responsive

基本上這邊遇到的不同手機顯示會有點差異的問題會比較大,就算一樣是google chrome,但是在iphone和在android顯示就是會有不同,這邊我個人其實就是換不同的css寫法,達到一樣的排版效果試試看,切記開發者工具的介面只能參考參考,一切還是以實體機為主,畢竟開發工具的模擬無法完全正確,這裡主要我遇到有一個神奇的bug? 應該說個人猜測是瀏覽器解釋行為不同,用手機看這網站,原本右上角漢堡排,是要過一段時間才會突然從畫面右邊跑進來,一個很怪的現象,但是iphone卻除了這個以外,我的header做邊LOGO卻整個往上移跑版了,一直找不到原因,但是過了一天後,我卻突然靈光一閃,想到是不是我那個漢堡排從右邊影響到了LOGO排板,經過各種分析後,我看到唯一的盲點是,我的header width是用 100%,我以為body的100%是螢幕所視範圍,但是iphone的表現行為讓我感覺body的width是變動的,個人猜測是我寫的動畫內容有擠到,所以我就把width改成 100 vw,竟然就真的解決了這個問題,但是我不知道為啥android上面原本就是正常,然後這個專案我有故意一直用flex來進行排版,感覺flex支援性已經很不錯了,反而有些用flex來排效果還比較好,相較之下反而用 % 好像還比較容易有神奇bug。

另外對於fullpage這個套件,因為手機browser有個討厭的地方,就是你只要往上滑後,瀏覽器的header就會自動隱藏,但是fullpage其實在一開始的時候就會自動計算寫死,每頁的高度,當瀏覽器header自動隱藏後,就會發現高度沒有滿版… 關於這邊我只好使用比較髒的手法, height: 100vh !important,強制覆寫掉inline style。

退而求其次

基本上各平台版面調完後,我也大概預料到應該就到deadline了,事實上也的確如此,因此svg動畫那邊,我就沒有特別花時間去設計了,畢竟那邊若要講求一點,可能也要弄掉一天的時間,加上在手機是另外一種呈現,才讓我覺得動畫部分就先暫時擱置,感覺做專案趕時程就是這樣囉,原則上當然以能夠做越多為優先,但是也要量力而為,能夠做出來的就要有品質保證,而不是每個都隨便做都保證有做出來。

總結

其實我主力於後端,前端我也只是拿我其他程式語言的背後實力,過來騙吃騙喝,哈哈! 對於css算是摸了不少屬性和特性,但是對於怎麼寫個好結構的css目前尚未有個結論,js方面其實這個專案我並沒有用什麼架構,畢竟沒多複雜,但是其實若時間上允許的話,我建議各位是可以多用用pure js拉,瀏覽器已經越來越支援了,除非你想要支援ie8…那種,你可以看看不需要jQuery,我大學是個jquery愛好者,因為跨瀏覽器實在太好用。

要是時間充裕下我個人會偏好使用es6 + mithirl js + i18next,剩下功能就手動來吧XD 只是我只能說很多很多時候,由於時間因素必須有所抉擇,react真的有點過於肥大…當然往後時代網路越來越快的情況下,可能也不算什麼了吧XD,至於svg好用的js套件,snap我不知道該不該建議,他問題不少,除了最新版本(0.5.0)那個超大bug外,還有個cpu 100%的問題,效能吃很兇,這邊或許該找找其他library來替代,不過等我之後想對於svg有更深入的研究會再來寫個部落格,這篇就大概記錄到此囉,重點部分都大概列出來了。

Mithril 體驗

Posted on 2017-03-10 | In javascript |

Mithril 體驗

Intro

Mithril 是個輕量的javascript library,相較於react, vue使用起來更加簡單,官方文檔也算是寫的簡單易懂,首先來個最簡單的示範

1
2
3
4

var p = document.createElment("p");
p.textContent = "Hello World!";
document.getElementById("output1").appendChild(p);

這個在mithril會相當於下面語法

1
2
3

var p = m("p", "Hello Middle-earth!");
m.render(document.getElementById("output2"), p);

m()會創造virtual HTML elements, m.render()將會把virtual dom轉換成real dom,這基本上和react是有著差不多的性質的,但是通常會使用component的方式,component就是一個javascript notation object,就是一個plain object,render會去抓view property,下面示範

1
2
3
4
5
6
7
8
9

var dog = {
view: function(){
var say = "wong";
return m("p", say);
}
}

m.mount(document.getElementById("output3"), dog);

像上面使用m.mount不用自己在額外呼叫m.render這個方法,另外m.mount參數是接受component,m.render則是需要vnode。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

var default_value = "default";
var input_hidden = false;

function change_saying(s) { default_value = s; }
function toggle_saying() { input_hidden = !input_hidden; }

var awesome_test = {
view: function(){
return m('div', [
m('button', {onclick: toggle_saying}, 'toggle saying'),
m('hr'),
input_hidden ? null :
m('input', {
onchange: m.withAttr('value', change_saying),
value: default_value
}),
]);
}
}

m.mount(document.getElementById('container'), awesome_test);

上面這範例值得注意的地方是,m.withAttr這個方法,他在input onchange時,會把其當下value傳入function裡面,所以你在執行範例會看到就算你切換掉input時,他裡面得值會依然保留,因為他在他被毀掉時之前就把值存起來了,下一次被創造就使用它,你可以試試看把onchange那行砍掉,在試著切換掉input,是不是永遠一旦切換回來input裡面的值都是default。

接著來看看呼叫api,並且得到回傳值後,rerender

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

var url = "http://ratfactor.com/misc/lotr-fellowship.json.php";

var Fellowship = {
res: null,
data: function(res){

if(res){
Fellowship.res = res;
}else{
return Fellowship.res;
}
},
};


function get_fellowship(){
m.request({method: "GET", url: url})
.then(Fellowship.data);
}

function view_fellowship(){
console.log('enter view_fellowship');
if (!Fellowship.data()) {
return m("p", "Click the button and wait.");
}
return m("p", Fellowship.data().description);
}

Fellowship.view = function(){
return m("div", [
m("h3", "The Fellowship of the Ring"),
m("button", {onclick: get_fellowship}, "Load Fellowship"),
view_fellowship(),
]);
};

m.mount(document.getElementById('container'), Fellowship);

api呼叫完後,畫面會更新主要是因為,m.request結束後會呼叫redraw,到目前為止其實大致上概念都跟react小像,接下來回頭從整個mithril架構來開始觀看。

Structure

其實也不該說是架構,應該要說是特性吧,畢竟他也沒有強制說要你使用哪種寫法,寫起來十分自由呢,官方文檔的key concepts那邊大概列了下面幾項

  • Vnodes
  • Components
  • Lifecycle method
  • Keys
  • Autoredraw system

Vnodes

就是指virtual dom,這邊效率會好的重點是,dom的修改所花費的效能,其實不如直接重新創造還比較好,當然mithril自己說他有改良virtual dom演算法讓他效率更高這邊,這邊我就沒特別往裡面看了,畢竟演算法不是目前要注意的重點

vnode types

  1. Element
  2. Fragment
  3. Text
  4. Trusted HTML
  5. Component

Components

如同前面所用到的,就像下面示範的

1
2
3
4
5
6
7
8
9
10
11

var Example = {
view: function(vnode){
return m('div', 'hello'+ vnode.attr.name)
}
}

// 這是一個component,跟react一樣我們也是可以傳入參數讓他去展現,
// 因此上面才會用vnode這個東西

m(Example, {name: "king"})

另外他跟react一樣也是擁有所謂的lifecycle,畢竟就是一個創造破壞dom的概念我想有lifecycle也是很正常吧~

Lifecycle methods

Keys

是一門技術用來將序列節點裡面的dom元素重新定序,並且將特定資料項目對應到相對應的dom元素序列,簡單的說這個keys就是用來讓virtual dom技術可以知道什麼資料對應哪個virtual dom元素。

就拿官網的例子來看,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

var people = [
{id: 1, name: "jax"},
{id: 2, name: "janna"},
]

function userList(users) {
return users.map(function(u){
return m('button', u.name);
})
}

// 這將會產生
// <button>jax</button>
// <button>janna</button>

那麼今天people變成 [{id:2, name:'god'}],render就會變成 <button>god</button>,那麼對於virtual dom演算,他會不知道要怎麼辦,因為有兩種可能,一個是第二的button被刪掉或是第一個button被刪掉,然後再改變其text的值,然後就會發生可怕的事,他自有自己的演算法去決定要怎麼下去,但是萬一今天你的button上面有綁事件怎辦? 你無法確定哪個button是被留下來的,就會產生錯誤,因此這也是為啥要有keys的原因,所以上面應該要修改成如下,

1
2

return m('button', {key: u.id}, u.name);

Autoredraw system

其實就是rerender的機制,在m.mount, m.route這兩個上面有相關機制存在,m.render沒有,以component為例,

1
2
3
4
5
6
7
8
9
10
11
12
13

var MyComponent = {
view: function() {
return m('div', {onclick: doSomething});
}
}

function doSomething(e){
// 這邊將會產生rerender,如果doSomething被呼叫後
// 如果你不想產生rerender,可以
// e.redraw = false
// 這樣就不會rerender了
}

m.request呼叫完畢也是會造成rerender,跟上面一樣他也有設定可以讓他不redraw

1
2
3

m.request("/example/api", {background: true}).then(function(){
})

最後route change也會造成redraw。

To sum up

整體而言,我覺得是可以嘗試一下這個library,因為他的自由性,個人目前有意思想把之前用angularjs寫的專案改成用這套寫寫看,不用react是因為,相對起來這套需要變動的地方比較少,他不像react通常還要搭配一些bundle tool還有架構性的framework才會比較完整,mithril js就是一個小而巧的library,可能也可以算是framework了,之後等我試用了一段時間後,有更多心得會再另外寫blog。

Reference

一些靈感和內容來自於下面部落格參考,寫得很詳盡清晰

  1. blog ref1
  2. blgo ref2

閱讀 Functional Programming Python 心得

Posted on 2017-02-28 | In python , functional |

閱讀 Functional Programming Python 心得

何謂 functional programming

通常是指程式語言具有以下特性

  1. function 是 first class object

意思就是 function 可以像 data(例:C語言的變數) 一般,可以被當作函式的參數傳入,也可以當作回傳值被回傳,或是被賦予到某變數,甚至是存到資料結構,

  1. 遞迴是基本中的基本流程結構,甚至有些程式語言是沒有所謂的迴圈

  2. higher order function

這是指function被當作另外一個function的參數或是將function當作回傳結果

  1. 程式語言結構沒有所謂的statements 只有 expression

這個像是haskell就是這樣

  1. pure functional language 是沒有 side effects

所謂的side effects是指,你的function,只要你每次的呼叫都是帶入同樣的參數,就會得到相同的結果

相信稍微熟悉python的人,看了上述就知道python其實也多多少少算是functional programming有點沾到邊,不得不說python就是一個這樣的語言,他並沒有強制要成為某種特定形式的語言,像是Ojbect oriented language,或是像C那種imperative language,甚至是functional language,所以這讓python在寫程式的時候有著很大的自由呢~

相關閱讀資源

書中有提到一些資源,這邊做點記錄

  1. python 官網

沒想到官網竟然就有一篇好教學

  1. ibm developer works

太久沒到這網站了,沒想到ui整個都改變了,這上面其實有不少教學文不錯,可以多看看

  1. awesome functional python

functional programming 思維

how to what

書中所提到的是functional programming,比起imperative programming,更加著重於 what,感覺上就是指,函式裡面是如何產生結果的這邊不用去理解,你只要知道你丟什麼東西,會產生生什麼東西就好,其實就是一種細節隱藏的概念,以下引用書中範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

collection = get_initial_state()
state_var = None
for datum in data_set:
if condition(state_var):
state_var = calculate_from(datum)
new = modify(datum, state_var)
collection.add_to(new)
else:
new = modify_differently(datum) collection.add_to(new)

# Now actually work with the data
for thing in collection:
process(thing)

上面程式就是典型的imperative思維,一行一行的指令執行,從獲得初始資料,資料剖析處理整理出你要的資料
,最後才真正地對資料去做要做的事情,再來看看functional programming思維是怎麼樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

def make_collection(data_set):
collection = get_initial_state()
state_var = None

for datum in data_set:
if condition(state_var):
state_var = calculate_from(datum, state_var)
new = modify(datum, state_var)
collection.add_to(new)
else:
new = modify_differently(datum)
collection.add_to(new)

return collection

# Now actually work with the data
for thing in make_collection(data_set):
process(thing)

程式碼幾乎沒啥改變,就只是單純多了一層抽象化包裝,但是這要表達什麼? 就是我們可以不用去注重make_collection裡面到底是如何做的,我們只要知道餵它吃什麼,它就會吐出什麼XD , 對於書中最後面 for 迴圈如果想要更佳functional思維的話,我個人覺得可以改成 [process(thing) for thing in make_collection(data_set)] 當然前提是process(thing) 是有要回傳資料的話~

這種思維對於unit test也是比較易於測試的,因爲測試本身就是一個你給它資料,然後你期待得到什麼回傳結果的行為,剛剛好等於functional思維。

遞迴

我想這是絕對最最最基本的程式流程,不會用到loop,對於python而言,遞迴是非常沒有效率的,而且有最大遞迴次數限制

compare recursive and iterative

上圖雖然我用的數字不大(因為試過隨便帶個1000,遞迴版本就爆掉拉),可以看出迴圈執行效率是比較高的,其實照理說一般是會對所謂的tial-recursion做編譯優化讓他等於迴圈,但是python並沒有做這種處理,因此這也讓我對python是否適合拿來學習functional programming感到一點遲疑,這也是為何我會好奇讀這本書的原因,然後有一點讓我挺驚訝的是,沒想到用reduce的效率會比迴圈高,真是不知道python的reduce裡面做了什麼,雖然說使用了dis模組看了一下解譯後的指令,的確是挺少的拉,但是這不能代表效率,我想如果想要知道為何效率比較高的話,可能得去研究python的source code了。

Closure and callable instance

書中有個說法十分有趣,如果是資工系相關出身的人大概都有聽過class is data with oprations attached 之類的說法吧,但是對於closure呢? closure is operations with data attached. 這句真是有醍醐灌頂之效,我真的沒這樣想過,以下舉例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class Adder:

def __init__(self, n):
self.n = n
def __call_(self, m):
return self.n + m

add5 = Adder(5)


def make_adder(n):
def adder(m):
return m + n
return adder

add5 = make_adder(5)

上面class版本只是示範,如何使用class來實作類似效果而已,那麼看看這個跟以前物件導向有哪裡不同,以前物件導向,就是初始化實例,然後他本身會有定義相對應動作,在呼叫去執行,所以就是class is data with operations attached,那麼今天這個class Adder則是已經先把行為給定義好了,剩下餵資料而已,就像是add5(10)就會得到15,這就是closure is operations with data attached 想要表明的意思。

執得注意的地方是python的closure,他的variable是bind by name而不是value,以下舉例

1
2
3
4
5

test_case = [lambda: i for i in range(5)]

print(test_case[0]())
print(test_case[1]())

沒意外的話你會看到兩個都是4,會這樣的原因是lambda: i 相當於 return i的意思,那麼這個i就是for迴圈的i,當for 迴圈結束時,i值等於4,所以由此可以知道python的closure變數只是做一個類似指標指過去的概念,所以如果你想要讓裡面的i值是每一次的for迴圈值那麼你就要用python會對function的parameter做初始化的動作那邊下手,如下

1
2
3
4
5

test_caes = [lambda i=i: i for i in range(5)]

print(test_case[0]())
print(test_case[1]())

這樣你就會看到你想要的結果拉~

lazy evaluation

這個在函式語言可是非常常見,簡單說就是遇到了才做動作的意思,比方說像我想要建立一個無限的陣列,聽起來很詭異吧,但是在haskell之類的functional programming卻是很正常的東西,因為他們本身就是lazy evaluation,需要從那陣列拿多少東西再拿,而不是一開始就先定義好一個內容無限長的陣列,在傳統的C語言,想必大家都習慣,new array,collect需要的記憶體這些動作了吧,哪來的無限哈哈~

在python中,要如何建立這種概念的東西,答案就是要使用iterator,其中最常使用的方式是,generator,應該很常看到在funtion裡面有yield吧,那就是generator,在python概念,會比較跟c++不太一樣,c++是有分container和iterator,iterator就是一個純粹的指標,指向container,然後可以存取東西這樣,可是python的iterator,感覺就比較像是兩者融合了,以下面範例來看

1
2
3
4
5
6

def infinite_ary():
n = 1
while True:
yield n
n += 1

PEP定義片段是這樣描述的 The iterator provides a ‘get next value’ operation that produces the next item in the sequence each time it is called,基本上就是有實作 next 這個magic method,讓他可以取得下一個值的都算是iterator,所以這麼看來上面這個function同時算是container和iterator的結合,但是其實也不能算是container拉,因為它實際背後也沒有真的去做收集資料,而是前面所講的lazy evaluation,然後最後還是回傳給你iterator讓你操作拉~ 其實用起來滿簡潔的,這也是為用了python回不去c++的原因,語法實在是太簡潔漂亮,反觀c++越來越…四不像了,也複雜的恐怖,已經好久沒碰c++了啊,哈哈哈

Currying

書中提到python內建模組中functool的partial,就是currying,我覺得這句有點不那麼精準,根據haskell 官網的定義,然後這是partial的定義,partial是指能夠傳入比函式定義少的參數的能力,以下舉例

1
2
3
4
5
6
7

def add(x, y):
return x+y


addOne = partial(add, 1)
addOne(2)

上面結果會是3,在haskell中,並不需要特別像python一樣,還要import functools中的partial,這是haskell基本特性,add函式本身定義是需要兩個參數,但是卻可以利用partial,給予少於兩個參數,並且得到addOne這個函式,然後我呼叫他,這個行為就是partial (apply),但是我這個舉例剛剛好也符合currying,接著讓我們來看currying定義是什麼,根據官方定義是,將擁有多參數的函式,變成只接受一個參數,如果還有其他參數,就回傳另外一個函式

1
2
3
4
5
6

g: (a, b) -> c

;; curried 後

f: a -> b -> c

上面是用haskell表示curry的概念,根據這定義我們前面python所寫的,也剛好符合,但是這不代表python實作的partial等於currying,因為今天我們要是給予兩個以上參數,或是任何地方跟定義不一樣,就會變成不是currying,所以應該是這樣說的,currying是partial的子集,我們可以利用partial達到currying的效果這樣。


總結

後半部書中,大致上都是在講python內建的,functools, itertools,確實使用起來挺方便的,也讓我反省了一下,是否以後在工作有些地方可以使用看看,先姑且不論效率上是否會更好,程式碼簡潔程度會有大大的提升,然後對於python來學習functional programming style,如果是以學習來講的話,我個人覺得不是那麼好,最好還是學haskell這種pure functional programming,會學的比較多和深,雖然我自己也還沒學特別深拉XXD。 但是python是可以用functional progamming style是無庸置疑的,而且老實講語意上也是很順,之後如果有時間我想我會好好想想怎麼用python來用functional programming的。

閱讀 20 Python Libraries You Aren't Using (But Should) 心得

Posted on 2017-02-18 | In python |

閱讀 20 Python Libraries You Aren’t Using (But Should) 心得

此書是oreilly 提供的免費書,上面還有許多免費的書,有興趣的人就自行去觀賞一下。 這本書如題主要是要講一些好用但較不常聽到或是被你忽略的library,首先以standard library切入,其實python built-in library 實在太豐富了,我想其實很多人連一半的模組可能都沒用到呢~ 以下開始介紹

Standard libraries

下面記錄一些

collections

collections.OrderedDict

python的其中之一data type dict,dict是不會保持順序的,也就是說你去traverse他,順序是不一定的,但是相信有時候在寫程式時會有需求是要用特定順序去探訪資料,這也是為啥OrderedDict會出現,下面給個範例。

1
2
3
4
5
6
7
8
9
10
11
12
13
import string
from collections import OrderedDict

[x for x in zip(string.ascii_lowercase, range(5))]

# 為了要看出dict是不會保持順序的,這邊我們故意用dict在包裝zip的內容
# 你可以試著比較下面兩個輸出結果

dict(zip(string.ascii_lowercase, range(7)))
OrderedDict(zip(string.ascii_lowercase, range(7)))

# 將會發現 OrderedDict 仍然保持著順序,OrderedDcit保持的是一開始的順序
# 也就是traverse遇到的順序 a, b, c, d, e ,ouput時就是會依照這順序

這個library實用性挺高的,我個人在工作上偶爾都會用到,需要詳細了解的他整個功能其實建議直接看他源碼實作,官方文檔介紹的比較少

collections.defaultdict

相信各位使用dict時會遇到常常要先檢查某key值是否在dict中,有的話就會某某動作,沒有的話就做一些類似初始的動作,下面用範例來將上面敘述變得比較容易懂

1
2
3
4
5
6
7
from collections import defaultdict

s = [('yellow', 1), ('blue', 2)]
d = defaultdict(list)

for k, v in s:
d[k].append(v)

上面將為每個dict的key所對應的值,先預設初始為空list,因此你才可以直接call append這個方法,要不然一般的方式是

1
2
3
4
5
6
7
d = {}

for k, v in s:
if not k in d:
d[k] = []

d[k].append(v)

這個我到是就用得比較少,實際上也只是減少你的coding數目,讓他更簡潔,要不要使用這個lib,個人覺得倒是沒有那麼大必要性

collections.nametuple

哈這個我想應該很多人都知道這個才對,對於程式碼可讀性,個人絕對支持一定要用namedtuple,不只是讓你自己寫程式方便,也讓別人讀你的code更好懂,以下舉例

1
2
3
4
5
6

from collections import namedtuple

tup = (1, True, "red")
Spec = namedtuple('Spec', 'count enable color')
tup_new = Spec(count=1, enable=True, color='red')

namedtuple的好處就是我不會是用magic number來存取值,像是tup這個tuple,想要取值就會用tup[0]去取第一個值,那麼對於tup_new 我就可以使用tup_new.count取得第一個值,可讀性哪個高馬上立見高下。

contextlib

這個主要是用於resource clean up會比較常用到,像是

1
2
3

with open('test.dat', 'r') as f:
data = f.read()

使用with這語法後,就不用在自行呼叫f.close(),因為他會在你裡面區塊執行完畢後,自動幫你執行資源釋放,這個應該很多人都有用過,真的特別的地方是,contextmanager

首先先來講講 with 後面所接的物件,這個你是可以自定義的,要自己實作class,並且實作__enter__,__exit__,詳細的可以看PEP343,這邊簡單的說明一下,with這語法就是為了將try catch finally語法,做個算是更優美語意。

這篇提案有很多想法都是來自於pep340, pep346,經過一段修修改改後才終於討論出一個定案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

with EXPR as VAR:
BLOCK


mgr = (EXPR)
exit = type(mgr).__exit__
value = type(mgr).__enter__(mgr)
exc = True

try:
VAR = value # only if "as VAR" is present
BLOCK
except:

exc = False

if not exit(mgr, *sys.exc_info()):
raise

finally:
# the normal and non-local-goto cases are handled here
if exc:
exit(mgr, None, None, None)

上面列出部分pep的內容,with的語法大概等於下面那些一長串的東西,語法顯得更加簡潔吧~

但是這個是class層級的物件,假設你今天想要用function而已,那麼要怎麼做呢? python contextlib,裡面所實做的 contextmanager 就是為了這個需求而生,進而跑出一些有趣的寫法~

這本書舉個有趣的範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

from time import perf_counter
from array import array
from contextlib import contextmanager

@contextmanager
def timing(label):
t0 = perf_counter()
yield lambda: (label, t1-t0)
t1 = perf_counter()


with timing('Array tests') as total:
with timing('Array creation innermul') as inner:
x = array('d', [0]*1000000)

with timing('Array creation outermul') as outer:
x = array('d', [0]) * 1000000

print('Total [%s]: %.6f s' % total())
print('timing [%s]: %.6f s' % inner())
print('timing [%s]: %.6f s' % outer())

上面所做的就是去計算妳函式運算花了多少時間,很方便,同時也很讓人耳目一新吧,從範例的使用方法,也大概略知contextmanager是使用python generator去實作出來的,所以使用時才會有yield這語法,這範例有個有趣的地方,就是 lambda: (label, t1-t0) 我一開始其實對這個地方有點不解,因為照理說當程式執行到這行時,它應該會不懂t1是什麼而報錯才對,但是事實是不會喔~ 因為程式執行到yield時就會停住,lambda 那串還不會跑喔,等到你call next 他才會開始繼續執行下去,至於為何要多包一層lambda,這邊就是因為t1定義是在後面,要是你不這樣多包一層,直接 yield t1-t0 就會錯了喔~

另外,array 這模組就是能夠創造像c語言裡面的那種array,雖然應用情況在哪我不太清楚,我真的幾乎沒啥用到這個,不過這個範例到是讓我知道對於array而已直接丟一個大list進去反而效率會比較差… 倒是讓我長知識了。

atexit

在書本後面某範例有用到一個內建模組atexit,這個我也是沒用過,但是感覺挺新鮮的或許在某些地方可以用到,看官方文檔,簡單說這個模組的功用是可以在你的整個程式要結束時,通常是用來作為程市意外結束時,為了避免資料遺失還是做個保存,或者是資源釋放吧,目前大概想到這些。

Third Party Libraries

這邊有些套件就不特別介紹了,只挑幾個個人有興趣做記錄的套件

Fabulous

一個滿有趣的套件,讓你的開發terminal介面程式,可以更加漂亮的客製化

Pywebview

底層應該是使用webview的引擎之類的像是webkit去渲染html,js,css,建立出原生gui元件,比起一般的gui或許會更好用,但是我個人的話,會選擇用electron,畢竟他的community比較強大,比較多人維護,不過electron就不是用python寫程式囉~~

ptpython

這個老實講我覺得不是那麼需要拉,如果你懶得開editor或是ide,這個可以讓你在termial的repl享受到稍稍接近ide部份功能,雖然我覺得ipython就很夠了,我還是列上來記錄一下XD

boltons

boltons算是python builtins的增強吧,不過其實python本身內建模組就已經很夠了,所以這套件個人是覺得不太有必要,但是卻是一個可以拿來學習的套件

Cython

這個特別挑出來講的原因,其實也是因為我一直以來都有聽說這個但是卻沒有花時間來玩玩感受它的魔力,我另外還有耳聞過numba,只是這篇就先著重於cython來記錄改天再來挖時間比較這兩者差別。

總體上來說,他是一個算是小型語言語法上幾乎與python差不多,經過Cython interpreter會把code轉成c/c++在compile成python extension modules,來讓python使用

安裝cython

十分簡單的安裝方式 pip install Cython

cython process

.pyx file compiled by Cython to .c file and then .c file compiled by c compiler to .so or .pyd accroding to your operating system. There files can be import directly by python

哈,上面只是cython編譯流程,反正你只要知道.pyx是給cython看得就好,接著來看看官網的簡單範例。

hello.pyx

1
2
3

def say_hello_to(name):
print("Hello %s!" % name)

setup.py

1
2
3
4
5
6
7
8

from distutils.core import setup
from Cython.Build import cythonize

setup(
name = 'Hello world app',
ext_modules = cythonize("hello.pyx"),
)

使用distutils來產生library檔,輸入python setup.py build_ext --inplace,下完指令後,就會產生library檔,之後就可以在你的py file import使用

ex.

1
2
3

from hello import say_hello_to
say_hello_to('stranger')

其實就算你不用cython也是可以達到相同作用,但是就是用c/c++的語法去寫library,或是你用swig之類的工具也可,cython需要再多多使用才知道實用程度如何,不過有一點讓我覺得不錯的是,他跟ipython有結合,其實官網範例都有示範,詳情可以去看看~

ipython 有分很方便的 % 這是執行單行command line %% 這是執行整個cell的command
舉例你今天想要簡單分析某個程式碼所耗時間就是使用 %timeit range(10000) ,那麼你有很多行想要分析的話,就要使用 %%

個人有用ipython寫個簡單測試cython提升效率的測試,為了要知道效果所以就用個最簡單寫法同時也是效率最不好方式實現Fibonacci數列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

def factorial(n):
if n==1 or n==0:
return n
else:
return factorial(n-1)+factorial(n-2)
# above version will reach the maximum recursion

def gen_factorial(n):
# I think this will be much more efficient than recursive version
first, second = 0, 1

for i in range(n):
yield first
first, second = second, first+second

上面第一個寫法就是效率最爛的寫法,使用遞迴,原因是會造成多次重複計算,最簡單提升效率方法是,用個table記錄已算過的值,類似cache的概念,這樣效率就會大大提升,但是數字一大最終會遇到一個問題,就是遞迴次數有最大限制,會爆掉,所以我下面提供了用generator,同時也是目前個人覺得效率最好的方式,也不用怕遞迴次數上限,轉回正題,今天使用cython要來比較效率改善多少,就是要來改第一個版本

1
2
3
4
5
6

def cfactorial(int n):
if n==1 or n==0:
return n
else:
return cfactorial(n-1)+cfactorial(n-2)

這邊我只是加了,型態宣告而已喔~~ 呼叫 factorial(33) 以我個人電腦大概接近兩秒,然後呼叫 cfactorial(33) 大概0.5秒,讓我嚇到了呢,原來型態沒有確定的狀況會造成效率這麼大的差異呢~

個人是覺得可以先試著用ipython來嘗試cython,玩一段時間後再來決定是否拿來應用在工作上。

python

Django development note 2

Posted on 2017-01-12 | In django |

Django development note 2

繼上次django development note1後,已經過了一段時間,咦~其實好像是挺長的時間,哈哈,總之呢,這段時期,前端技術方面,實在是苟日新,日日新,又日新, 近來sharp edge技術都是由前端那render頁面(就是指用js去渲染html),有不少的事情,已經慢慢從後端被拿到前端去負責了。

其中像是

  • html render
  • url router
  • i18n
  • ..等等

但是django 本身有個template system, 就是由後端來render出頁面,我在公司嘗試過,將angularjs和django結合寫過公司官網,另外也試過reactjs和django結合,寫出電商網站,但是這邊的url router和部份html render其實都還是用django的system,其實並沒有做一個完全的前後端分割,沒有這麼做的原因是考量到seo的問題,傳統由server端渲染頁面的話,seo就不用擔心,因為爬蟲都爬的到,但是改成由js渲染的話,問題就大了,因為爬蟲不會解析js並且去運行,已目前的爬蟲來講的話啦,雖然有聽說,google爬蟲未來會有支援,但是未來是未來,我們要著眼當下是吧~

因此url router是由django負責的話,我可以考慮哪些頁面是需要seo,先由後端給出這樣~ seo這個問題這麼大,像React他們是怎麼解決的呢? 他們有所謂的render server,原理大致上,就是server先講js的邏輯run過,產生頁面,再回傳給client端,說是這樣說啦,但是你用了React+webpack後,尤其是css的buidle問題,就會跑出一堆啦,因為許許多多webpack套件當初只是為了frontend而寫,沒考慮到拿到backend是否能run,所以其實render server沒那麼簡單。

個人研究後,看到universal概念,就是為了上述問題… 而產生的, 其實有個範例叫universal react redux 啥的,就有用render server,老實講實在挺恐怖的,只要一扯到universal,不知道為啥裡面整個用到超多套件,邏輯複雜.. 設定也麻煩, 明明React超級夯,但是都人沒提出一個漂亮解決seo問題的方法,後來我和同事有稍微搞個universal react redux with django的測試專案,要的話可以到這邊參考,老實講現在要我回去我應該很多都忘了xd 太多小套件了,在實作這專案期間其實有個叫做next.js的東西,號稱zero setting的render server,實際上個人使用也的確是這樣,但是她有個很大限制,就是你不能自己寫webpack config!! css也是只能寫在js檔裡,這也難怪它是zero setting囉~ 但是我想等以後這個套件就會越來越方便啦,值得期待。

經過在公司一年多以來的經驗,我後來自己整理了一些後端project starter架構,個人而言我覺得後端可以開始只要做為純api server,也就是說url router,template system,有的沒的都可以去掉了,專心設計api,剩下的都搬到前端去,這麼做的好處在哪? 至少是有下面幾點

  • 前後端程式碼邏輯分割清楚

有些人會想前端跟後端程式碼哪裡可以扯在一起? 基本上只要你用的backend framework有template system,一定會有不少邏輯混在後端,然後前端在一起搭配,之前進入公司發現一堆magic code和bug都超難找,就是因為太多邏輯四散各地 :(

後端變為純api後,前端頁面有所問題,毛頭出在哪,就往前端找阿xd,不用在霧裡探花,驀然回首原來bug就在經過處,每次都要仔細找後端哪裡暗插了什麼鬼怪code,遇過暗插inline css的,整個囧.. 我想光是這個優點,就讓許多人開心了吧。

  • 前端不用懂django

跟前面很像,因為分割清楚,加在環境也是分割,因此前端不需要知道django是怎麼運行,自己就可以搞環境,若是以前用法,前端需要懂一點django,才知道怎麼run測試server,甚至還要migration你說是不是~ 身為一個前端還要懂後端,豈不是很麻煩? 無法專心在前端上

  • 前端開始具有架構性

由於webpack或是gulp之類的工具存在,開始讓前端具有一定的結構性,其實這是好事~
但是這麼分割後,前端的架構究竟該怎麼弄會比較好? 這邊我還不知道xd 畢竟我本分是後端嘛~ 雖然我略懂一些js和css, 最近我會研究一下前端架構,有一定心得後,我改日必定放上 :)

說了這麼多,好像都還沒提到django development相關心得,哈哈,前面都只是一些經歷讓我整理了後來的django as pure api server架構,下面來進行大綱性的解說

專案架構

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
api/
....
..
core/
settings/
wsgi/
__init__.py
urls.py
wsgi.py
nginx/
nginx.conf
requirements/
dev.txt
prod.txt
Dockerfile
docker-compose.yml
manage.py
circle.yml
pytest.ini

這是一個簡略的專案架構簡介,想要更詳細的了解,可以去django as pure api server了解,從這邊可以知道我至少用了docker和docker-compose還有circle ci的東西,這部份跟持續整合比較有關係,其實目前來講關於docker這邊,用到的地方除了未來持續整合那邊可以用到外,就是我本地可以先進行模擬正式台環境,確保不會有意外出現,我自己在開發中其實還是用django的runserver啦,其實真的是最方便,當然有興趣的人可以使用docker-compose一下,但是對於debug時,我暫時還沒想要怎麼弄xd 因為平常debug django app時,我都是用python -m pdb manage.py runserver,那麼使用docker-compose時我還沒研究要怎麼attach process裡~~

個人覺得docker是一個值得投入心力去學習的工具

比如像我個人是使用elementary os作業系統,個人寫了一個安裝腳本,之前我要測試這腳本是否能夠正常運行,我都會用vmware player,灌elementary os,來進行,但是頗麻煩,而且感覺有浪費空間,有了docker後,由於elementary os loki,是基於ubuntu16.04,所以這邊我只要使用ubuntu16.04 image,來進行測試,run個container,就可以跑一下我的腳本看看過程是否有錯誤這樣,測完就可以砍掉啦~清爽~

再來對於nginx和uwsgi設置,這邊我也可以用docker來進行模擬,而不用真的灌在自己主機上,要不然自己主機越灌越多東西,會變慢的,只能說docker真的是一個很棒的工具,也難怪她後來夯成這樣。

docker-compose就是一個輔助你使用docker的工具,原理就是寫一個設定檔(yml),她會自動幫你建立環境,省去你打超多docker指令和開啟一堆terminal哈哈。

pytest

pytest的話,就是拿來執行測試用的套件,老實講要怎麼寫出好的測試,我目前無法給出一個心得,因為這實在是太難囉~ 這邊需要讓我花時間研究一下

再來使用的套件方面,以下列出個人覺得值得一提的

  • django debug toolbar

是一個挺不錯的工具,對於sql的效率分析很有幫助,除了sql以外,她其實有許多分析和記錄,但是我不常用。 然後請注意,在production請拿掉這個套件,這個會吃掉你不少效能,這跟python built-in module profile是一樣意思,這種分析的套件都是插入執行分析,必定會影響你程式運行效能

  • django modeltranslation

對於model資料有i18n,覺得這套件不錯,因為有個很大的重點他不會破壞你原本的model,之前是使用django-parler,雖然當初用的時候覺得不錯,但是這個是使用繼承的方式,所以會破壞到model的一些特性,而且她有個很大的麻煩處,就是無法使用model inheritance,幸好當初我用的case只是文章性質的需求,所以不用用到複雜的功能

  • django rest framework

這個應該不用多解釋了吧,用django的各位,想要實作rest api,多半應該都是用這套了吧,我之前文章有一些心得,可以去翻翻~

  • django rest swagger

就是一個可以當作api doc和測試的頁面,說實在的目前用起來就是一種缺少文檔的感覺,所以有些地方實在還不知道怎麼去調整,但是整體上而言算是不錯用,讓你的文檔,變成互動式的,而不是以前那樣呆板的文件

最後

對於django要不要升級到1.1x版,其實我還在考量,但是未來勢必是要升級的啦,因為他是沒有向後兼容的升級,這感覺就是當初python2跟3的抉擇,目前而言我選擇在django<1.9 , 是一個穩定修復bug的版本,但是我目前用到的專案是有故意選的,大致都有支援到django 1.1x後,所以要升級其實應該是不難,只是尚在考量中(其實是懶 :p

tags: django python

Spacemacs 使用心得

Posted on 2016-12-16 | In editor , spacemacs |

Spacemacs 使用心得

前言

身為一個programmer,一定會用過許多不同的editor or IDE,目前用過感覺不錯的就大概是下面這幾個,

  • atom
  • visual studio code
  • sublime text
  • spacemacs

以上順序並沒有按照好用程度安排,上面是列出比較general use的,像對於python而言,當今算是頗強的IDE,應該就屬pycharm了吧,不過這就不列入心得,這邊是註記general use而且偏向editor。

上面這些其實我都有用過才列出來的,最剛開始我是用sublime text,在他剛出來的時期,真的是輕巧,好用,就算是現在應該也不少人在用啦,對於我本身是用python開發的,也有套件支援一些比較基本的 code completion, find usages 等等功能, 只是之後就出現基於electron開發的editor, vs code和atom這兩者都是,這兩個都不錯用,我後來也從sublime text轉到atom,原因其實很簡單 :) 因為atom的theme十分漂亮,畢竟electron嘛~ 可以輕易用css刻介面,而且老實說插件品質也不錯,另外sublime text感覺已經幾乎沒啥重大更新了,另外關於vs code我只能說如果你的專案是前端相關的,後端又是用nodejs,那我想這個絕對非常符合你的需求,另外專案如果很肥,用vs code會比較順,許多朋友用過也是說vs code效能比較高,甚至有朋友說他電腦用atom,有時候會當@@ 我個人是沒遇過啦~ 這邊就不引戰 :p

或許你們會說用一個編輯器熟悉熟悉就好,幹嘛學那麼多套,其實我也這樣想過~ 其實我當初以為atom我會用挺長的時間,只是莫名的我發現spacemacs,試玩過後,我發現似乎回不去了xd 可以先跟你說說atom可是有插件特意學他的快捷方式呢~ 首先就先來大致上的介紹一下spacemacs

正題 spacemacs

一進去官網你會看到大標寫說,他是community-driven,好啦,現在應該大都是這樣了,重點是接下來你會看到的三個單字

  • ergonomics
  • mnemonics
  • consistency

第一個人體工學? 很好,我當初看到也覺得只不過是一個編輯器,是在人體工學啥? 這邊個人認為是快捷鍵的問題,emacs一直以來被人詬病的就是快捷鍵充滿的Ctrl or Alt的prefix,就是說你得先按ctrl or Alt的輔助鍵再按其他鍵,實在很難按~ 按久了手指可能會有問題吧,雖然一堆emacs神手說,好按、有邏輯,但是我用了不到一個禮拜就先放著了xd 阿! 這邊忘了說,其實emacs我很早有碰過,但是沒特別繼續深入就先放著了,實在上手度太高,那時候反而是學vim先,學了一些基本的text object move, edit的東西,這邊也造成我後來要用的editor都一定要有vim mode,實在不得不說就是好用! 編輯快速!

第二個記憶法,這邊指他的快捷設定是有邏輯的設定, 舉例: 他的快捷開頭是要先按 SPC (空白鍵)

  • 要看buffer list,就按 SPC b b
  • 要看project的buffer list,就按 SPC p b
  • 要看project list, 就按 SPC p l

看到這邊大家迫不及待想試試到底有多麼厲害吧~ 接著就來安裝

安裝方法其實不難,首先先裝好emacs,然後github有教怎麼安裝spacemacs,其實~ spacemacs說穿就是一個幫你整合各方好用插件,然後自己本上有個類似插件loading system去管理的 emacs 設定檔, 所以安裝就是把github整包丟入 ~/.emacs.d,簡單明瞭,接著就是開始emacs,就會自動下載安裝啦,過程中會問你的什麼習慣忘了xd 反正就是選擇, 印象中是會有問你想要使用 evil 嗎? 這邊我當然選是啦~ evil 就是 vim-mode, 老實講 evil 是我用過,模擬vim快捷,模擬最齊全的套件了,真的很強,他連vim的ex mode都有, 舉例來說就是 1,3d 把一到三行的內容刪掉這樣,evil 可是可以做呢~

安裝完畢後,當你按下神聖的SPC ,你就會看到下面有彈出提示如圖preview img , 這是其中之一的原因讓我回不去,我只能說這太屌了,我不用硬是把各種快捷功能記下來,我只要用mnemonics way,去記憶相關大概鍵就好,剩下都會有提示,實在很棒,然後其實emacs功能實在是太多, 其實不是每個功能都會配置在 SPC x x 上面,這時候你就可以用 M-x (M是指meta key通常是指Alt鍵啦),不知道mac上是怎樣喔~ 我沒用過mac, 按下 M-x,可以進行功能搜索,打出你要的功能名字就可以使用~ 這也是我很喜歡的功能!

再來就是emacs的window manager和buffer的概念,讓你檔案之間跳轉阿,編輯,或是查看code,文檔之類的十分好用, 幾乎是只要開啟emacs後,就可以做幾乎任何事了,甚至emacs內建 terminal emulator,算是不錯用,雖然個人目前還是幾乎在作業系統上的terminal做事比較習慣。

preview image
上圖示範就是window隨意切割,另外鼠標跳轉也很方便,SPC w [H J K L], 左 下 上 右, 視窗間跳轉,spacemacs本身功能就算是非常齊全了,想當然爾身為一個好~~~用的editor,一定都有所謂的plugin系統,這邊只要SPC f e d, 就會跳去設定介面,在spacemacs裡是叫做layer

preview image

上圖就是內容,如果你想要讓你的editor,對python擁有像ide般一些功能,就把python加入後並儲存,之後要進行refresh, SPC f e R,等待下載安裝完成就可以開始享用啦 :)

第一次用emacs很多疑問?

我只能說是的,基本上網路上教學不少,但是沒很深入! 可以先簡單看過後,邊用邊學,然後其實emacs本身就充滿文檔說明,所以他也常常被說 self-documented, 你可以按 C-h,就會看到下面跳出,提示,按下相對應鍵,就會看到針對什麼功能做說明, 我常用的是 C-h b ,可以幫你找到對應功能的key binding, 另外有一點很重要,有些模式下按? 可以得到提示按鍵說明,像是你在neotree視窗裡面按下? 就會有說明,比如按 c 就可以創造檔案之類的,是個好用的訣竅。

再來就是可以針對emacs的某功能進行網路搜尋解說了,因為emacs其實本身就是用elisp寫的,其實本身就是個超級插件大集合啦xd , 所以一定會有相關說明,當然如果你本身懂lisp,我想你大可直接進入源碼,或許會看懂不少東西? 我是沒看過啦 哇哈哈~

目前工作上使用方式

因為我是用python開發,version control是用git,所以其實跟spacemacs剛好也很搭拉xd 尤其是spacemacs用magit,這個實在是超級好用!!! 為此特別稍微介紹一下magit,我只能說真的讓我不用在terminal打那麼多東西啦 :) 不過用慣IDE的人就看過去就好xd

magit

實在不得不說,這個真的是一個另我對gui介面類型?git的軟體有所改觀的東西,以前用過source tree類型的軟體,算是好用啦,但是說實在的,專案一大每次loading都要一段時間,而且操作彈性實在不大~ 或者其實是做的到的,只是不好找怎麼點,說到這裡你一定還是很好奇,magit厲害在哪? 其實應該這麼說,搭配spacemacs+evil,實在好用,舉例

首先SPC g s,這樣就會進入mgit模式,接下來呢? 不知道可以按什麼鍵對吧? 記得前面講過的在很多模式下你可以按?,就會跳出提示,如下圖的右下角

image

有看到s快捷指的是stage吧,再來對於這功能來測試看看

選擇我想stage的檔案

只要進入visual mode,選到你想選的檔案,再按s鍵(快捷鍵),就相當於 git add [files you choose]

藉由類似這種搭配,像是commit,diff之類的,其實視窗跳轉都在emacs內,非常方便,而且一堆git指令都有綁定按鍵,像是pull from origin,按個 p u 之類的, 讓我雙手保持不離開鍵盤,同時又可以讓我非常快速的進行version control,以前我都是用terminal直接打指令,老實講打到手好酸阿xd 哈哈,magit就像magic一般,我沒想過可以享受輕鬆的按幾個鍵,就可以做完這些version control。

可惜的是目前只有git有這麼棒的套件,mercurial個人是還沒看到~

python

spacemacs本身就有個python layer,做了挺完全的功能支援,尤其是虛擬環境挺容易啟動的(, V a),追尋源碼(使用evil的話,快捷是g d十分方便),還有對於ipython repl的一些send code buffer的功能,這個好用,對於想要即時試用部份代碼的功能是否正常運行很方便,個人常用, s R 這會將我目前選取的code傳進ipython的repl。

debug的話,目前個人還是在terminal裡使用pdb, emacs本身是有作結合啦,就是GUD,讓你像一般ide debug那樣的fu,但是我沒有特別使用,所以這邊就不多敘述了。

總結

我這邊講到的還只是spacemacs冰山一角,他的layer(plugins)實作非常豐富,對於我工作上,開發效率是有所提昇的。唯一比較讓我比較卡的地方可能就是,因為emacs都是用elisp寫的,所以你要改插件或自寫插件會比較麻煩~ 除非你本身就滿懂lisp的, 總之呢,spacemacs還有很多東西可以去碰,去學,像是macro, undo-rings 有的沒的,現在我也才用spacemacs大概兩個月,而且也沒特別深入學習,因此暫時只能記錄這些心得。

tags: editor

初嘗react native

Posted on 2016-08-29 | In react native |

初嘗 React Native

學習動機

有鑑於最近公司比較沒事,所以就心血來潮來玩一下,其實這還算是玩具性質的專案,有著效率上的問題,而且應該會有不少地雷存在,雖然他說得很漂亮,什麼write once, run anywhere? 當然我覺得這不是不可能,只是大概需要很常的時間,才能做到,不過呢,反正時間多出來了,就來玩玩看也不吃虧。

環境設置

這邊是以Linux(ubuntu 14.04)為例,官網其實都有教學,只是在ubuntu上面通常安裝相對於,mac和windows來講都會比較麻煩點.. 我也不知道為什麼,linux的東西,一直以來懶人包好像都比較少,不像其他平台都是點一點就安裝好啦~~

安裝node環境那邊很簡單就不多說, react native command line 安裝方法也滿簡單的如下

1
npm install -g react-native-cli

主要是 Android Studio這邊,首先呢! 你必須先安裝一下java環境,最好是oracle的java, ubuntu還有個open java,我想畢竟android是用oracle java開發的,所以最好還是用oracle吧,當然有人想試試open jdk的,試玩再告訴我心得 :)

1
2
3
4
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer
sudo apt-get install oracle-java8-set-default

至於安裝jre環境這邊我是用

1
sudo apt-get install default-jre

java環境這邊告一段落, 再來去android studio官網下載安裝檔,接著根據官方說明文件~ 請先做一個步驟

1
sudo apt-get install lib32z1 lib32ncurses5 lib32bz2-1.0 lib32stdc++6

這個是你系統是64bit ubuntu的話,再安裝這些,看起來他是需要32bit的library,所以才安裝這些,接著剛剛下載完的檔案,沒意外應該要是一個zip檔,就把他解壓到你要的地方吧,一般而言會是放在 /opt/下面,你爽就好 :p

1
sudo unzip android-studio.zip -d /opt

這樣就算是安裝完成。

1
/opt/android-studio/bin/studio.sh

上面就是啟動方式, 不過啟動程式這樣很麻煩對吧, 你想要的話,其實可以把 /opt/android-studio/bin/ 加入系統路徑, 這樣就可以簡短指令, 你只要 sutdio.sh 這樣就可以啟動了。

不過我個人會比較想要建立一個 Desktop file 來啟動,就是像是桌面捷徑的感覺,點兩下就啟動程式,以下是Desktop file的內容

1
2
3
4
5
6
7
8
9
10
[Desktop Entry]
Version=1.0
Type=Application
Name=Android Studio
Exec="/opt/android-studio/bin/studio.sh" %f
Icon=/opt/android-studio/bin/studio.png
Categories=Development;IDE;
Terminal=false
StartupNotify=true
StartupWMClass=android-studio

把檔案存在 ~/.local/share/applications/ 下面,這樣就可以建立捷徑了~

Watchman這個他是沒說一定要裝啦,只是看起來裝了這個,開發上效率似乎會比較好,所以就都安裝啦, 哈哈哈哈阿, 這裡我選擇直接install from source 的方式,最簡單明瞭 :)

再來差不多可以往下進行啦~

Reactive Hello World

雖然官網就給個幾行指令,說這樣就可以啟動example了

1
2
3
react-native init AwesomeProject 
cd AwesomeProject
react-native run-android

但是~~~ 事實果然就是沒那麼簡單喔~ 人生總是期待越高,失望越大。 疑! 突然好像有點悲觀(題外話

這邊要注意的是,請記得先啟動你的Android Studio,他會安裝android-sdk相關的東西,這邊需要注意版本, example 使用的是 23 版, 但是啟動時,我的是先安裝 24版的,所以記得安裝一下,安裝完後, 記得設置 ANDROID_HOME這個環境變數,

1
2
export ANDROID_HOME=$HOME/Android/Sdk
export PATH="$HOME/Android/Sdk/platform-tools:$HOME/Android/Sdk/tools:$PATH"

我的配置如上, 這樣一來大致上就沒問題了~~ 大概…吧

可是! 我錯了, 哈 第一次用關於android的東西,看了錯誤訊息,似乎是他需要先運行一個模擬器呢~ 所以我選擇是Genymotion公司的android員工說是用這個,說效率比較好,所以就先採取他們的意見啦 :)

哈~ 不過呢! 後來先換成用google的模擬器吧, Genymotion在ubuntu 15 以下版本,安裝有點步驟,頗麻煩… 首先先看看你的模擬器有哪些裝置,當然這邊你也可以自己建立一個模擬器

1
2
3
android list targets
android create avd -n react -t 1 --abi default/x86_64
emulator -avd react # 啟動模擬器

值得注意的是有許多地方都需要依靠 android sdk manager,來安裝一些tool~

啟動完模擬器後,雖然官網說執行 react-native run-android 這行就可以了,但是沒意外你應該會出現紅色錯誤,官方自己下面也補充了,請額外執行 react-native start 這行,這樣一來就可以看到成功畫面了,如下

screenshot

但是有個問題,你會發現這個模擬器並沒有和我們的鍵盤連線,也就是你按了鍵盤毫無反應,也就無法享受到他給你的double tap R,更新的效果,所以接著我們要解決這個問題, 嘖嘖在linux上問題總是許多,我是不知道其他平台也是不是會遇到這些問題啦~~

重點在,要先啟動 avd manager, 這邊我找超久… 原來不是在android studio裡面,直接在command line裡面輸入android avd就可以打開了..我都快要翻桌了!! 接著設置允許keyboard,之後就可以享用按兩下R就更新的快感啦! 哈哈哈哈哈哈

好啦,起手式就到這邊為止,再來我要先來繼續看基本教學,有了一定心得後,再來po一篇文章。

tags: react-native
12…7
jing

jing

63 posts
26 categories
Github Facebook
© 2018 jing
Powered by Hexo
Theme - NexT.Mist