Tag

2017年10月7日 星期六

論共享

其實這篇文章7月底就寫好了,只是一直都沒有寫完整貼出來,現在共享經濟已經慢慢進到衰退期,新聞也沒這麼多了,貼出來單純就是留個記錄XD

隨 obike 進駐台灣,讓台灣自 Uber, Airbnb 之後,再一次體會一個共享的商業模式,撇開 obike 違停造成的爭議不談,有一派的說法是,共享其實毫無新奇之處,飯店就是共享豪宅,圖書館是共享冷氣…等等。

我認為共享其實是個新概念,而不如上述所說,只是一個很潮的新名詞。

在過去其實是沒有共享,而是<公用>和<租用>的綜合,提供方是政府公家單位,亦或法人企業,就如上例中的圖書館、飯店,要服務大量使用者,清潔和維護都需要成本,因此會出現一個單一節點的單位為管理者。
與過去的公用及租用不同,現今的<共享>是運用科技,將管理的成本降到極低,同時允許一般民眾都能成為服務提供者,Uber, Airbnb 屬於此類,Uber 直接結合 GPS、刷卡付費、手機App 等科技工具,跳過管理計程車的規則,讓每個人都能成為計程車司機;Airbnb 則是允許任何有閒餘房子的人,上網登記提供給任何有短期需要的背包客。

兩者的重點都在於,透過網路大範圍的招募供應方和租用方,這和過去的公共提供,有單一節點的供應方截然不同。

就我這個定義來看,共享單車是否可稱為共享其實是存疑的,畢竟任何共享單車仍然有對應的管理單位,而非共享每個人擁有的腳踏車,我家裡有多的腳踏車,我並沒有辦法上網登錄,然後把它丟到路上讓大家使用,使用現行的腳踏車,一樣要向公司繳交押金、使用費用(對…其實就租金),這跟 Uber, Airbnb 所謂的共享,仍有一段差距。

目前共享在各地引起的爭議有兩種,第一如 Uber,在於直接挑戰各地政府對於出租車的規定;第二如共享單車,在於企業出租腳踏車大量佔有公眾資源(ex,違停佔用機車格、人行道),造成社會整體效能下降,引來政府管制。
共享單車的爭議其實不算太複雜,本來基於社會最大福祉,維護公有資源的使用量,政府就會對市場上的交易有所管制(我知道我寫最大福祉會有點爭議…不過我們姑且相信是這樣,才能討論下去),例如路邊停車格的設計,就是限制路邊(公共空間)的使用,以價制量,並對不守規矩的人拖吊,才不會變成馬路上都停滿車子;另外像是客運路線,政府也會介入,管制單一路線由哪些業者經營,以免熱門路線被大客車佔滿,偏遠路線卻無業者經營。

資源愈是珍稀,管制的範圍就會愈廣,由政府單一節點介入管制的機會也愈高;Obike 的爭議,充其量只是它把原先公眾免費的資源,使用到盡乎枯竭,引來政府介入罷了。

當然,這裡面牽扯的,又有更大程度的問題,像是政府規範的界限,政府究竟要無所不包還是儘量退縮?以 obike 的案例來看,這條界限是浮動的,取決於資源是否已經稀有到,需要政府介入分配,以免造成社會整體利益減損。
裡面牽扯的,包括我一直很好奇的,如日本的大手私鐵(ex. 阪急電鐵),它們在城市內建設地下化車站的時候,究竟是如何進行公眾相關的工作,例如土地取得、出口用地取得、交通維持等,這類具有稀有性和排他性的公眾資源使用。
我不認為一般的私人公司有這樣的權利進行這樣等級的資源分配,但若是由私鐵請求政府協助進行,豈不是政府直接圖利廠商?在市區內鐵道這種高度珍稀資源,難不成竟然是私鐵先搶先贏,建成之後完全排除了其他私鐵競爭的能力?想起來多少覺得有點怪怪的。

總之,針對共享經濟,我認為這是科技對社會又一衝擊,每每都在挑戰社會的接受度和容忍度,以及政府畫定管制界限的位置,除了完全禁止和完全開放之外,我們會需要更多的民眾討論,動態的進行實驗,我想是比較有效的解決方式。

2017年8月25日 星期五

使用rust closure實作fizzbuzz

之前用Rust 重寫Understanding Computation 裡面的ruby code,目前從 github 上來看,我的 Rust code 應該是僅次於原作者的 code,完成度最高的一個版本。
從去年五月,把大部分的 code 完成以來,唯一一個沒寫的章節:chapter 6 的 fizzbuzz,最近終於實作出來了\weee/。

本來我是用了比較直接的方法,也就是把closure 用function 來實作,使用generic 的方式來處理參數,例如在數字的部分,我們就要接受一個函式跟一個參數,這個函式要吃一個參數然後吐一個參數……。
例如我那時候實作的正整數的部分:
fn ZERO<F, T>(p: F, x: T) -> T where F: Fn(T) -> T { x }
fn ONE<F, T>(p: F, x: T) -> T where F: Fn(T) -> T { p(x) }
fn TWO<F, T>(p: F, x: T) -> T where F: Fn(T) -> T { p(p(x)) }
fn THREE<F, T>(p: F, x: T) -> T where F: Fn(T) -> T { p(p(p(x))) }
fn FIVE<F, T>(p: F, x: T) -> T where F: Fn(T) -> T { p(p(p(p(p(x))))) }

這個寫法的問題是啥?問題在於…我必須手動處理所有的型別,這在只有數字、布林的時候還容易處理,等到型別一複雜,這種函式宣告根本寫不出來,然後編譯器噴你一臉錯誤,最終完成的也只有number 跟 boolean,甚至連下一段的 is_zero 都實作不出來,程式碼我保留在 ch6-fizzbuzz 的分枝裡。

最近有天心血來潮,把我的 Rust code 實作成果貼到 rust forum:
https://users.rust-lang.org/t/computation-book-example-code-implemented-in-rust/12403
在討論串的下面有一位 jonh 回了我,他的辦法挺聰明的,實作的方式也比較符合這個 project 的要求,首先呢,我們不要處理這麼多型別的問題,把所有的型別都收到一個enum 之下:
pub enum Pol {
    C(Rc<Fn(Rp) -> Rp>),
    I(i32),
    B(bool),
}
pub type Rp = Rc<Pol>;
macro_rules! r {
    ($cl:expr) => {Rc::new(Pol::C(Rc::new($cl)))}
}

impl Pol {
    pub fn call(&self, x: Rp) -> Rp {
        match self {
            &Pol::C(ref c) => c(x),
            _ => panic!(),
        }
    }
}

型別 Rp 是用 rust 的 reference count pointer (Rc) 包裝這個 Pol 的型別,Pol::C 則是包裝一個 Rc 包裝的函式,該函式會吃一個Rp,吐一個Rp,等於是封裝了一個 lambda 函式。
另外我們利用自定義的 macro,讓產生這類封裝的 lambda 函式更容易,最後我們定義呼叫的 call 函式,它會把 Pol::C 裡的函式取出來,用 c 取用參數 x 執行。
這樣,就完成了函數的基本型態。
接著我們就能跟著這本書,一步步打造 fizzbuzz 的程式碼,例如上面提到的正整數的部分:
let zero  = r!(|_p| r!(|x| x));
let one   = r!(|p: Rp| r!(move |x| p.call(x)));
let two   = r!(|p: Rp| r!(move |x| p.call(p.call(x))));
let three = r!(|p: Rp| r!(move |x| p.call(p.call(p.call(x)))));
let five  = r!(|p: Rp| r!(move |x| p.call(p.call(p.call(p.call(p.call(x)))))));

這樣寫的問題是,我必須把所有的 closure 定義寫在 main 函式裡,因為 rust 不允許以 use 的方式,引入定義在別的檔案的 closure,以致最後 main.rs 高達 600 多行。
第二個問題是由於Rust 的所有權特性,在定義每個 closure 的時候,會需要不斷的 clone,例如 multiply 的 closure,需要用到 add 還有 zero,所以我們就要一路把 add 跟 zero clone 下去,寫到複雜一點的closure 例如 divide,需要使用 if, is_less_than, increment, subtract, zero,一個closure 的定義橫跨40 行,這寫法我覺得真的不行,不過一時之間真的找不到更好的寫法。
// multiply
// |m| { |n| { n(add(m))(zero) } }
let multiply = {
  let add = add.clone();
  let zero = zero.clone();
  r!(move |m: Rp| {
    let add = add.clone();
    let zero = zero.clone();
    r!(move |n: Rp| {
      n.call(add.call(m.clone())).call(zero.clone())
    })
  })
};
最後,我沒辦法把 Rp 這個函式印出來,像書裡面印出橫跨數頁,狀觀的lambda函式,這個問題也暫時無解。

最後的成果,完成的 fizzbuzz 所下所示:
let solution = {
  map.call(range.call(one.clone()).call(hundred.clone()))
   .call(r!(move |n:Rp| {
     _if.call(is_zero.call(module.call(n.clone()).call(fifteen.clone())))
        .call(fizzbuzz.clone())
        .call(
          _if.call(is_zero.call(module.call(n.clone()).call(three.clone())))
             .call(fizz.clone())
             .call(
               _if.call(is_zero.call(module.call(n.clone()).call(five.clone())))
                  .call(buzz.clone())
                  .call(to_digits.call(n.clone()))
             )
        )
   }))
};

執行起來慢的要死,fizzbuzz 1-100 費時 51s ,如果真的用 rust 寫,根本不用1 ms 好不好。
當然了,最終能用 rust 把這篇奇文給實作出來,還是覺得滿有趣的,中途也曾出現過,因為一個括號括錯地方,瞬間讓 multiply 變成 power 3*5 = 243,WTF!我至今還參不透,究竟為什麼括號括錯就會讓 multiply 瞬間升一級變 power OAO
我的程式碼都收到master branch 下面,可以參考 github連結,體會一下 functional programming 的奧妙之處XD
這篇文其實根本是「重新發明輪子的極致」,不止是演算法,我們要把整個整數系統、真偽值什麼的,都重新打造一遍,有一種我們先來種顆樹,長出來之後砍下來變木材,作成工具台之後開始打造輪子,感情我不是在寫 fizzbuzz,而是在玩 minecraft 呀(X。

2017年7月29日 星期六

設定 ssh讓人生簡單一些

ssh 是工作上的重要工具之一,平時要連進其他電腦、傳送檔案、甚至是上 ptt 都可以用 ssh 達成,這篇文整理一些 ssh 的設定,可以讓 ssh 的使用更簡單一些。

第一個是免打密碼的設定,這個如果有用 github 的話一定很清楚,簡單來說我們可以把電腦的公開金鑰放一份到遠端,登入的時候 ssh 就會不問密碼自動驗證。
步驟如下:
在家目錄下的 .ssh 目錄中,鍵入 ssh-keygen 指令,預設是使用 rsa,如果覺得 rsa 不夠安全可能會被心算解開(誤,可以用 -t dsa | ecdsa | ed25519 | rsa 選擇要用哪種公開金鑰加密法,用 -b 來選擇生成的金鑰長度(不過我下了 ssh-keygen -b 16384 然後它金鑰生不出來…)。
金鑰生成後,會詢問檔案要存在哪,預設就是 .ssh 資料夾;另外會問 pass phrase,我們都用上 public key 就是不想打密碼,除非有安全性考量否則留空即可。
Enter file in which to save the key (/home/garbage/.ssh/id_rsa): /tmp/id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
會產生兩個檔案,id_[algorithm] id_[algorithm].pub,從檔名看 pub 自然是公開金鑰了。
接著將 .pub 檔案的內容,複製到伺服器上 .ssh/authorized_keys 裡面,如此一來 ssh 就能免密碼登入了

詳細請參考
https://blog.gtwang.org/linux/linux-ssh-public-key-authentication/

第二個是設定 ssh config,這有點像 ssh 專用的 /etc/hosts,可以幫常連的機器設定別名,甚至是連線時要用什麼動作,以下來看看:
首先,假設我們要連線一台電腦,remotemachine.com 或者是 ip,帳號名稱是 yolo,開的 port 為 9453,要連線要打入這樣的指令:
ssh yolo@remotemachine.com -p 9453
當然如果照這篇文章安裝了 fzf ,某種程度上能大幅緩解連一台機器要打很多字的問題--連機器的指令不會變,用fzf 找出來就是了,但每次都用 fzf 來找還是會花一些時間,這時可以改用 ssh config
在家目錄的 ~/.ssh/config 檔案中,加上以下內容:
Host MyMachine
 HostName remotemachine.com
 User yolo
 Port 9453
ssh MyMachine 就會直接連上 yolo@remotemachine.com -p 9453
這些規則不是寫死的,ssh 的設定優先次序是命令列、.ssh/config、/etc/ssh/ssh_config,設定後照樣可以用 another@MyMachine 來使用其他使用者登入,或者用 -p 來改變連線的埠。
設定檔就是許多 Host 為開頭的區塊,內容為針對該 Host 的設定,上面展示的就是設定預設主機、使用者跟埠,還有許多其他設定可用,像是認證檔的位置、選擇加密方式,詳細可以看 man ssh_config

下面是我查到 ssh config 的契機,平常工作的地方開了一個新的工作室,但平常測試的主機A放在工作室A,兩個工作室的網段不一樣,沒辦法透過 ip 直接連線;為了連線所以在 router 上面鑽一個洞,開一個 ip 會直接進到工作室A的另一台主機B,到主機B就進到內網,可以直連測試主機A。
如果要打指令,大概會長得像下面這樣,等於是透過machineB ,開一個 pseudo-tty,再執行 ssh進到machineA:
ssh -t userB@machineB.ip -p 9453 ssh userA@machineA.ip
要簡單一點找到這個 stackoverflow,關鍵字是ssh proxy,在 .ssh/config 裡面加上這些設定
Host machineB
  Hostname machineB.ip
  User userB

Host machineA
  User userA
  ProxyCommand ssh -q machineB nc -q0 machineA.ip 22
ProxyCommand 指定要連到這台 Host 時要下的指令為何,這裡會用 ssh quiet mode ,再執行 netcat 接入machineA,這樣只要用 ssh machineA 就能完全上述工作啦
當然這樣每次連線都要打兩次密碼,不想打密碼,照上面在機器上放入自己的公鑰即可。

2017年7月3日 星期一

使用 git-svn 和 svn 遠端協同開發

最近因為跟人協作,共同開了一個版本控制資料夾。
對方使用的版本控制是 svn ,而我則是用 git,重新學 svn 實在太麻煩了,有沒有一個好的解決方案呢?經過強者我同學 AZ 大神跟 qcl 大神的推薦,決定使用 git-svn 來解決。

git-svn 是 git 提供的一個…橋接工具?可以在遠端保持 svn 的狀態,本地則用 git 的管理,享受 git 那些branch 開很大開不用錢,git stash之類,種種我們再熟悉不過的使用方式;另外用了 git 也不用每次都跟遠端目錄同步,可以在自己家裡亂搞,最後有網路時再一次同步。
畢竟在 git 出世前,svn 才是世界上版本控制的霸主,有不少早期知名的 project ,例如LLVM, apache software;用了 git svn ,不需重新熟習 svn 也能用 git 參與這些 project 的開發。

第一步,在拉下 svn repository 的時候,直接使用 git svn 的指令,所有跟 svn 相關的指令都是 git svn xxx:
git svn clone http://SERVER/svn/trunk/ TARGET_DIR
這樣就會把整個svn給抓下來,它同等於執行 git svn init 跟 git svn fetch。
要注意的是因為 git 設計的邏輯就是「所有的機器裡面都有完全一樣的內容」,所以 git svn clone 的時候,它會把遠端的內容逐個載下來,如果遠端 svn 很大的話,這個動作可能會花上非常久的時間。
抓下來的 repository會產生一個叫 git-svn 的 remote ,這個 remote 只有用 git svn 的時候會動到;要注意一點,因為 svn 只能維持一條線性的歷史,同時也沒辦法修改歷史,所以在使用 git-svn 的repository 裡面,不要和其他的 git 遠端同時使用,保持所有使用者都用一個 svn 遠端,git svn 設計上也假定你只有一個遠端。

再來我們就能做些修改,一樣就是git add, git commit,這時提交的內容只會在本地中,可以用:
git svn dcommit
把內容送到 svn 遠端去。
git 在推送到遠端 svn 的時候,會把一個一個 commit 取出,並提交到 svn,然後最重要的,它會依照svn 的提交結果,重新在 git repository 裡面 commit 這些結果,整個 dcommit 的結果,最終效果更像是 git rebase,這跟一般的 git push 完全不同。
commit aea3964417e62759dadf9e1769d927623e0f5a1b
Author: yodalee <garbage@mail.com>
Date:   Fri Jun 30 16:31:30 2017 +0800

    add debug message to every function call

commit 2531676d1d1b81f898e9965c0e46f28e92e02c82
Author: yoda <yoda@59464745-af19-4556-b8ec-ef3a2794439b>
Date:   Fri Jun 30 08:00:23 2017 +0000

    fix description in sensor function

    git-svn-id: http://SERVER/svn/trunk@2345 59464745-af19-4556-b8ec-ef3a2794439b
上面的 git log ,包含一個已經推到遠端的 commit 跟一個還未推送的 commit,推送到 svn 上的 commit 會出現 git-svn-id 的遠端資訊,同時它的作者資訊跟雜湊值也會變化,這也是為何不建議同一個 repository 中同時使用 git跟svn的遠端,git-svn 修改雜湊值會讓 git 遠端天下大亂。
就算要有 SVN 跟 git 兩個遠端也要先向svn dcommit ,得到最終雜湊值後,再推送到 git 遠端上。

svn 身為版本控制,也是允許其他人共同協作,只要有協作就會有衝突要解決,如果發生衝突,svn dcommit 會無法推送到遠端。
為了解決該問題,可以執行 git svn rebase ,它和 git pull 很像,首先它會用 git svn fetch ,把 svn 遠端上的內容拉下來,沿著 git-svn 往前長,之後再用 git rebase ,把現在 git HEAD 指向的目標,rebase 到 git-svn 上。
如果沒有更新的內容,在 git svn rebase 時會看到:
Current branch master is up to date.
此時就能放心進行 git svn dcommit

這裡會牽涉到一些 git 跟 svn 設計不同的地方,在 git 裡面,假設 remote/master 跟本地的 master 有所不同,在 push master 的時候即會發生衝突,git 會要求你解決衝突後才能 push。
svn 在這點上,只有檔案有所衝突的時候才會要求,所以當遠端修改 A 檔案,本地修改 B 檔案,在 dcommit 的時候是完全沒有問題--只是遠端專案會進到一個 A, B 檔案都修改過,而本地檔案卻沒看到 A 檔被修改的狀態。
直接引用 git 文件的話:「如果做出的修改無法相容但沒有產生衝突,則可能造成一些很難確診的難題。」所以,誠心建議還是在 dcommit 前都 svn rebase 一下,確保跟遠端保持隨時同步。

其實有了 dcommit 跟 rebase,大概也就差不多了,有關 svn branch 的部分我就不太想看了,畢竟 git branch 比較強大;唯一要注意的,大概就是要送到 svn 伺服器之前,儘量用 rebase ,把 git 的各 branch 收整成一條線性,再進行 dcommit 。另外有個小技巧是,git svn dcommit/rebase 在操作的時候不允許任何 uncommit 的內容,所以在 svn 操作的前後,可以利用 git stash push/pop ,把未commit 的內容塞進stash,svn 操作結束後再取出來。

參考資料:
Git 與 Subversion

2017年6月17日 星期六

Nand2Tetris 教學投影片與講解影片

故事是這樣子的,5 月的時候我把 Nand2Tetris 這門課給修完了。

剛好,我們同好們辦的 Code& Beer 需要題目,想說都修過了就來講(ㄔㄨㄢˊ)解(ㄐㄧㄠˋ)個 Nand2Tetris 吧,花了點時間整理了投影片,也要感謝在Yahoo 台灣大殺四方驚動萬教每月豪領100K的人生溫拿勝利組強者我同學 qcl 大神,幫我準備場地跟辦活動:

投影片slide share 連結:
https://www.slideshare.net/youtang5/introduction-to-nand2-tetris

錄影使用的是 Facebook 直播,為了把影片載下來花了點功夫,後來找到答案如下:
播放影片之後,按右鍵有一個「顯示影片網址」,將網址的 www 改成 m ,使用 mobile 模式看影片,這時就可以直接右鍵另存影片了:
http://www.green-umbrella.biz/2016/03/how-to-download-facebook-live-stream-videos-into-mp4-files/

當日後面接著 typescript 的活動,這是用 ffmpeg 剪輯過才上傳 Youtube 的:

講了一個半小時,很多地方還是講不清楚,畢竟本來的課程是兩門 coursera,各 6 週,每週的影片時間都是 2 小時以上,這已經是超濃縮版本了,如果大家有興趣還是可以自己修修看,會學到東西的。
不過我想投影片跟錄影完,也算是個總結,我跟 Nand2Tetris 的故事就到這裡了吧,畢竟這也是基礎課程,熟悉了就要往其他更困難的地方前進了。
Related Posts Plugin for WordPress, Blogger...