2012年4月27日 星期五

快快樂樂學會 Python decorators

這篇算是改寫自 A primer on Python decorators,不算是翻譯文,因為我懶得照翻 XD 不過例子跟文章結構都是參考這篇文章中擷取出來的。

引子

在 Python 當中,functions 是 first-class objects。也就是說你可以對 function 做任何跟其他 objects 一樣的事情。舉例來說,你可以把一個 function 指定到一個變數當中:

>>> def addone(n):
...     return n + 1
>>> addone(3)
4
>>> add1 = addone
>>> add1(3)
4

這樣看來可能沒什麼,但是當你可以把某個 function 傳入另外一個 function 之後再回傳時,我們可以發現這會有很多有趣的應用。舉例來說,在 Python 裡面有內建 map 這個 function,你只要傳入一個 function 以及一個 list,map 就會回傳一個新的 list,回傳的 list 內容是把你原本傳入的 list 當做輸入,傳進去 function 之後回傳的結果。以下是個例子:

>>> numbers = [1,2,3,4]
>>> map(addone, numbers)
[2, 3, 4, 5]

這樣可以幹啥?

我們透過了 map 這個簡單的例子來示範這麼做的好處,接下來我們來展示一個更棒的例子。

Fibonacci number 的定義如下:

Fibonacci number

Fibonacci seed

如果用 Python 來寫,你可能會寫出下面的 code:

def fib(n):
    if n in [0,1]:
        return n
    else:
        return fib(n-1) + fib(n-2)

這邊我們可以看到,當計算 fib(10) 的時候,會去計算 fib(9) 和 fib(8)。計算 fib(9) 的時候會計算 fib(8) 和 fib(7),這邊我們可以看到 fib(8) 的值被重複計算到了。所以我們希望把重複算過的值記錄下來,這樣就可以省下很多計算的時間,這個技巧叫做 memoization

要達到這個目的,一個簡單的想法是在 fib function 當中用個 dictionary 把算過的值記下來,如果要求的值已經算過了,就回傳存過的值,不過這方法麻煩的地方就是在於還要修改 fib 的程式碼才能達到這個效果。在 Python 當中,我們可以有更好的方法!既然 function 也可以當做回傳值,我們可以把傳入的 function 修改成會把結果記下來的版本再回傳,下面是這個 function 可能的樣子:

def memoize(fn):
    stored = {}

    def memoized(*args):
        try:
            return stored[args]
        except KeyError:
            result = fn(*args)
            stored[args] = result
            return result

    return memoized

這麼一來,我們要產生有 memoization 效果的 fib function 可以這麼做:

fib = memorize(fib)

這跟 decorator 有啥關係?

由於這種 pattern 常常出現,所以 Python 當中提供了 decorators 這個機制來讓你更簡單的做這種事情。

@memoize
def fib(n):
    if n in [0,1]:
        return n
    else:
        return fib(n-1) + fib(n-2)

在這邊,我們會說 memoize 是個 decorator,它 decorate 了 fib 這個 function。其實這邊做的事情跟上面的例子沒有什麼不同,只是透過了 @ 這個 operator 來減少了 coding 的麻煩。

在 decorator 當中,我們也可以傳入參數。舉例來說,如果我們希望用 memcached 來存已經算好的結果,我們可能會寫成下面的樣子:

@memcached('127.0.0.1:11211')
def fib(n):
    if n in [0,1]:
        return n
    else:
        return fib(n-1) + fib(n-2)

這樣寫其實就等同於

fib = memcached('127.0.0.1:11211')(fib)

結論

這邊很簡單了介紹了 Python 當中的 decorator,相信看完之後對 decorator 應該有些初步的了解。Python 當中 decorator 是個很常見的樣式,在各種 framework 當中處處可見。透過 decorator 的應用,將可以讓你的程式碼語義更加的簡潔易懂。

2012年4月24日 星期二

PHP:一個亂七八糟的爛設計 - (II)


續前

一些比喻

我對Mel隨口講了對於PHP的失望,而她堅持我必須我這些話寫在這裡:

我很難解釋PHP到底有什麼問題。好吧。想象你有一個工具箱,並且裡面存在着一組工具,看起來是可用的標準的工具。
你拿出一把螺絲起子,但發現既不是十字也不是一字。好吧,可能暫時沒什麼用。但你可能毀想說有一天應該會有用吧。
你拿出一把鐵錘,但令人沮喪的,兩頭都不是拿來錘釘子的。好吧。其實還是可以用,可以用兩個尖端中間的部分來敲釘子。
就這樣,每個工具都有有點奇怪甚至離奇,但也不至於完全沒用。畢竟整體來看好像該有的都有,也堪用。
現在想像如果你遇到百萬個工匠使用這樣的工具箱。並且他們告訴你:這些工具很好啊,我不覺得有什麼問題啊。這些工具我都有用過而且都可以用啊。但是看一下這些人蓋出來的房子,房屋呈現八角形,屋頂上下反過來了。敲一下門,就崩壞了,然後這些工匠開始指責你把門弄壞了。
這就是我看到的PHP的問題。

立場

我認為以下特質對於讓一個語言具有生產力或有用是很重要的。但PHP違反了這些特性。如果你不認同這些特質是重要的,我們大概就沒辦法再談下去了。

  • 一個程式語言必須是可預測的。這是一個中介媒體用來傳遞人類的想法,並且透過這個媒介來讓電腦實現這些想法。所以讓人類理解的程式是很重要的。
  • 一個程式語言必須有一致性。當我學習了其中的一部分後我應該可以很快地學會剩下的部分。
  • 一個語言必須簡潔。
  • 一個語言必須是可信賴的。語言是拿來解決問題的,所以必須盡可能避面帶來新的問題。
  • 一個語言必須要能夠被除錯。
於是我的立場是這樣的:
  • PHP總是令人感到意外:mysql_real_escape_stringE_ACTUALLY_ALL
  • PHP沒有一致性:
  • PHP不夠簡潔:=== 或是 C API呼叫時的錯誤處理
  • PHP有點古怪:==foreach ($foo as &$bar)
  • PHP很難理解:沒有堆疊追蹤
我沒有辦法為每個問題寫一段解釋來說明這些問題。我相信讀的人會自己思考XD


待續

2012年4月17日 星期二

幫你的網頁加上 backbone(.js) 吧!

Backbone.js 是當今頗受開發者喜愛的 Javascript MVC (Model-View-Controller) 框架 (framework) 之一 ,幾個知名的服務包括 Foursquare、LinkedIn Mobile 以及 Joel 團對開發的 Trello 等網站都已經上了骨幹 backbone(.js) 囉。

Backbone.js 提供了非常簡單的方式創建模型 (model) 和視圖 (view) 幫助開發者可以很自然而然的明確區隔使用者操作介面 (view) 的行為及背後資料處理 (model) 的邏輯,讓程式碼井然有序。

事實上,Backbone.js 並不是真正的 MVC framework,她並沒有 controller,但這卻不構成問題。通常 controller 比較適合於 client-server 的架構,controller 會攔截需求 (request) 以決定要調用哪個 model。以 Javascript 這種所有事情都跑在 client 端的情況來說,view 可以直接和 model 溝通而無需再透過 controller 。

Backbone.js 的 model 是透過 observer pattern 的方式,讓 view 可以直接監聽 model 上的任何 event ,並且立刻更新 view 本身。Backbone.js 也直接內建支援 jQuery 或 Zepto 操作 DOM。除此之外還提供了 collections (相當於 model 的 array),並提供支援 RESTful JSON interface 的 API。

構成 Backbone.js 的基本元素有底下四種類別:
  Model
  View
  Router
  Collection

我們會針對各種類別逐篇介紹,而這一系列的介紹文章取材自於 Joe Zimmerman 的 Javascript Blog 影片教學單元,我們希望能藉由擷取影片中的重點,讓大家便於學習、複習整個 Backbone.JS 的基礎觀念。把影片翻譯成文章不是一件很容易的事,若內容有不夠周詳完整的地方,也懇請不吝告知。

在開始之前,請先把 Backbone.js 以及其所依賴的函式庫 Underscore.js 加進來:
緊接著就讓我們開始吧~

Model 顧名思義是用來存放資料的類別,使用 Backbone 的 model 的好處是可以很容易地和其他 Backbone 的類別作互動,這部分從底下的例子可以先看出一些雛形。
廢話不多說,首先我們來新增一個 model:Backbone.js 的 constructor 的名字是 initialize:


可以直接新增 model 的屬性:


或是取出剛才新增的屬性:

console.log(person.get('name'));

也可以針對屬性給予預設值:


因此,底下 person 雖然沒有傳入預設的屬性,但透過 get 仍可以取得預設值:


除了對 Model 的基本 get/set 操作之外,我們也可以 listen 一些 event,像是 view 或是 model 的變化。底下的例子是當 "name" 屬性的 change 事件發生的時候,會把 name 印出來(請注意 change:name 之間不能包含空格,感謝 Miau Huang 的提醒)。


也可以透過 binding 機制檢查 model 的正確性,例如:


編按:根據網友 amos6064 的建議,event 的 binding 用 on() 會比 bind() 好。

除此之外,也可以很容易地把 model 轉成 JSON,像是:
console.log( person.toJSON() );

最後,是一個完整的範例:
以上是 Backbone.js 的 model 的基本介紹,希望大家會喜歡!

想進一步瞭解的朋友歡迎直接造訪官網站:

Happy Coding!


2012年4月16日 星期一

解決無法從Eclipse和ADB來安裝APK的問題

前陣子,正準備在HTC Desire的手機上除錯的時候,卻一直跑出以下的錯誤。

[2012-02-20 16:17:41 - XXX] Installation failed due to invalid URI!
[2012-02-20 16:17:41 - XXX] Please check logcat output for more details.
[2012-02-20 16:17:41 - XXX] Launch canceled!

跑去logcat也沒有印出個東西來,用ADB直接安裝,也會跑出[INSTALL_FAILED_INVALID_URI]錯誤代碼。

幸好網路上已經有人提供答案。
原因是HTC Desire的手機預設會把[/data/local]資料夾的寫入權限關閉。
(其它HTC型號 - incredible - 似乎也有類似的問題)

解法:
用command line執行adb.exe [路徑: android-sdk\platform-tools\],再輸入以下的指令。
adb shell
cd /data
chmod 777 local

再用Eclipse試一次,就可以成功的安裝APK到HTC手機上,開始漫無止境的除錯。

PHP:一個亂七八糟的爛設計 - (I)



最近讀了一篇外國人(Eevee)寫的批評PHP的文章,大概是我這輩子看過在同一篇文章中出現最多負面意思的英文單字的一篇文了。很難想像居然有人會這麼痛恨PHP到這種程度啊,更讓我覺得驚訝的是要對PHP痛恨到這種程度其實本身也是要對PHP熟到一種程度才能夠寫出這篇文。這麼有趣的文一定要來翻譯一下:

原文:PHP: a fractal of bad design

我是一個胡思亂想(或是有毛病的?)的人。我抱怨很多事情,這世界上有很多技術是我不喜歡的。基於程式是一門很極年輕的學科,我們之間甚少人真正知道我們自己到底在做什麼,所以這基本是可預期的。在加上Sturgeon定律告訴我們 -任何事情,其中百分之九十都是垃圾- 我這輩子還有很多值得我抱怨的。

但PHP不一樣,PHP不僅僅只是難用到靠背,或是不合我用,或是爛,或是違背我的信仰。基本上,這語言的所有功能就某從層面而言都是爛的。這語言,框架,或是整個PHP生態都爛透了。因為PHP爛的這麼有系統化以致於我甚至沒有辦法單純的指出一個點說為何我不喜歡PHP。每次我想要生一個列表來講PHP有多爛,我就會卡在這個深度搜尋的問題中,不斷的發現更多更多的可怕的(appalling)問題。

PHP是個令人難堪的東西,一個會導致我的職業毀壞的東西。但這居然被一堆還沒學其他的東西的業餘工程師歌頌著。這實在是非常令人抓狂。雖然它有一些微不足道的救贖特質,但是我寧願選擇相信它沒存在過。

我把這些我痛恨的東西從系統化的崩壞中抽取出來了,這是我最後一次嘗試了 ...

待續 ... (譯:這文章太長了,先翻到這好了XD)

2012年4月12日 星期四

你為了UI的功能性開始亂加Div及Class嗎?

在剛學寫網頁的時候,你一定會知道,元素上的class是拿來控制外觀的。

比如說,我們想要拿下面這個 button 元素來當作開關。



你當然會設定 btn 的css屬性來美化它的外觀。

但是,在這個JavaScript 當道的時代,class, 已經不只是拿來做純粹的靜態外觀了。我們一定會用它來做各種互動。比如說下面這個簡單的例子:



上面這份程式碼所做出來的開關就可以跟使用者互動,使用者的點擊會讓顯示文字在 on跟off之間切換 。

但光看DOM的結構的話,我們沒有辦法知道 div.toggle是有功能性的元素,我們可能會以為,這是一個有兩個子元素的一般div而已。

所以,為了讓這份HTML一看就能知道它大概的作用。我們可以把toggle改名成 js-toggle

這個命名小小的改變,讓我們觀察DOM結構的時候,不用先看熟自己Project 內的Javascript 就能知道js-toggle之後會在javascript中被使用。讓我們看第一眼HTML 就可以對這份文件的行為充滿想像力,擁有更豐富的語意。
分享這個一點小想法,希望讓大家的Project可以更好維護,更加茁壯:)

PS: 強者我友希望我幫忙廣告這個粉絲團XD

reference:
http://ozmm.org/posts/slightly_obtrusive_javascript.html
https://github.com/styleguide/javascript

2012年4月10日 星期二

什麼才是對的DOCTYPE宣告?

話說有一天我正在寫一段簡單到不行的HTML的時候,突然有一個問題非常的困擾着我。這段HTML是這樣的:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <body style="height:100%"></body>
</html>
就這麼簡單的一段,然後我直覺的認為body的高度一定等同於browser中inner window的高度。一定是吧,我都設定了body高度是100%了啊。幹!但不是,我想了一整天一直覺得Chrome有bug,但是後來發現IE/FireFox也是 ... 所以,媽的,這應該是我腦袋裡的bug ... 還好我後來遇到一個神人(Tim.yellow) ,他立刻就可以跟我說這是因為我的DOCTYPE宣告會讓瀏覽器進入Stirct模式(這模式的歷史來自於早期瀏覽器各家沒有完整遵守W3C spec的關係。因此後來有了這個模式是為了相容在當年錯誤的狀況下產生的網頁的兼容性。我的認知大概是這樣,如果有錯神人Tim會幫我修改XD) ,在Strict模式下,整個viewport的root是html。 所以為了讓這段簡單的HTML符合我的預期,必須幫HTML也加上height style才能夠影響整個viewport的高度:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html style="height:100%">
  <body style="height:100%"></body>
</html>

這樣寫就好了耶。果然是神人Tim。

或者是:
<html>
  <body style="height:100%"></body>
</html>
什麼都不寫,就會進入Quirks Mode,這時候的root是body,所以高度就真的會是100%。

感謝來自雲端的神人 Tim :)

2012年4月9日 星期一

用 Google Analytics 來做 Client-Side 的 Error Logging

本文翻譯自 Client-Side Error Logging With Google Analytics

前幾天在 Hacker News 上面有一篇鼓勵開發者記錄 client side error 的文章,這麼做可以讓你發現一些你的使用者遇到卻沒有告訴你的錯誤,為了達到這個目的,下面是一些可能的選項。

你自己做

如果你手癢的話你可以自己寫個 logging system。當 client 有錯誤發生的時候發個 jquery.ajax 到 server 端把你的錯誤給記錄下來。如果你寫了一個一堆 bug 的應用程式或是用了一些有問題的第三方的程式庫,這可能會對你的 server 造成很大的負擔。一種建議的作法是把錯誤集合起來而且過濾之後先在 client 端存到 localStorage,之後再一次送出。

如果你想要完整而且面面俱到的 logging system,自己動手做可能會是你最好的選擇。

Logging as a Service

有些 SaaS 提供了 logging 的服務,其中有幾個值得一看:

大部分的這些服務都提供了一定程度的免費試用,不過你大概還是得為你的程式付出每個月至少 10~15 元美金。

免費的 Google Analytics

在討論串中有人提出了第三種方法,是用 Google Analytics 來記錄這些例外。這可能沒有提供像上面提過的 SaaS 一樣多的功能,不過不用花任何錢而且設定十分容易。你只要簡單的修改 "window.onerror" 方法註冊變成 Google Analytics 的一個 "Event".

在幾個小時之內,你會看到在 Google Analytics 裡面看到你的 error。

這個方法為一個有點麻煩的問題提供了一個很簡單的解。

  • 你可能已經裝了 Google Analytics 了,所以你只要再加上 3 行 code
  • 讓 Google 可以分擔你的 server 負載,讓你可以把你的 server 拿出來做更多其他的事情。
  • 通過 Google Analytics,你可以追蹤 client-side 錯誤對其他 business metric 造成的影響。

這也不是那麼完美

這種作法有些很明顯的缺點,首先,要幾個小時之後 Google Analytics 才會收到資料,所以沒辦法即時收到問題。

另外,window.onerror 很鳥,它並沒辦法給你太多的資訊。如果你壓縮了你的 javascript,你也沒辦法得到正確的行號跟檔案名稱。

TL;DR

如果你不想花錢來買 SaaS Logging 服務,也不想花時間搞你自己的 logging server,你可能可以試著用 Google Analytics 來記錄你的 client 端的錯誤。

2012年4月5日 星期四

Non String Key for Associative Array in JavaScript

What is Associative Array

javascript 的 associative array 就是C++ 的unordered_map, Python的dictionary。 

Associative array 存取方便,效率高,Lua這個小巧的語言更是只提供了 associative array 來當作基本資料結構而沒有提供佔連續記憶体空間的 array. 

Associative Array in JavaScript

javascript 的 associative array 只要用以下的方式就可以使用了:
var mapping = { name : "tim" };
console.log( mapping['name'] );    // tim
console.log( mapping.name );      // tim

Key is Simple String

javascript 的 associative array 簡單易用,但是他只能以字串當作key。

請看以下的程式:
var o1 = new Object();
var o2 = new Object();
o1.name = 'tim';
o2.name = 'eve';
var mapping = {};
mapping[o1] = 10;
mapping[o2] = 20;
console.log(mapping[o1]);   // <--- 不合理的點!! 會印出20

在最後一行,就是奇怪的地方了, 你的key失去作用了。不管你用o1還是o2當作key, 你得到的數值都是20。

原因是javascript 不知道這怎麼把o1, o2變成 String. 所以一慮會把o1,o2 轉成 "Object"這個字串來當作mapping的key。

用toString來讓 Object 可以成為Associative Array的Key值


那我如何把這些Object 放進 associative array裡呢? 我們要為Object 提供 toString方法。

[o1,o2].forEach( function(d){ d.toString = function(){return this.name;}; } )

上面的程式,this的觀念覺得很不順嗎? 請參考這一篇文章 JavaScript的this,你有搞懂過嗎?
。 幫這幾個Object 加上 toString 的方法之後,就可以把Object 放進 associative array裡面了。
mapping[o1] = 10;
mapping[o2] = 20;
console.log(mapping[o1])  // 10
console.log(mapping[o2])  // 20
上面都是簡單的示意,想要讓程式更精鍊一點的話,建構物件時,你需要搭配javascript的 prototype才行!





2012年4月1日 星期日

JavaScript的this,你有搞懂過嗎?

JavaScript的this關鍵字隨處可見,但對this的誤解更常見  ...
一起來搞懂 JavaScript this 吧!

本文翻譯自 http://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/

你需要知道什麼

每一個執行的 context 都有一個對應的 ThisBinding ,並且這個 ThisBinding 的生命週期與執行的context等長,並且是一個不會改變的值。執行Context有三種:


執行context語法this 是
Globaln/aglobal object (e.g. window)
FunctionMethod call:
myObject.foo();
myObject
FunctionBaseless function call:
foo();
global object (e.g. window)
(undefined in strict mode)
FunctionUsing call:
foo.call(context, myArg);
context
FunctionUsing apply:
foo.apply(context, [myArgs]);
context
FunctionConstructor with new:
var newFoo = new Foo();
the new instance
(e.g. newFoo)
Evaluationn/avalue of this in parent context


  1. 全域 (global context)
    this 被綁定為全域的物件,通常就是 window

    alert(this); //window
  2. 函式 (function context)
    至少有五種方式可以執行函式,this 的意義依呼叫的方式不同會有不同的值
    1. 當作一個物件的method執行
      this 就是對應的物件
      var a = {
          b: function() {
              return this;
          }
      };
      
      a.b(); //a;
      
      a['b'](); //a;
      
      var c= {};
      c.d = a.b;
      c.d(); //c
    2. 沒有base的函式執行
      this 是全域的 window 物件
      var a = {
          b: function() {
              return this;
          }
      };
      
      var foo = a.b;
      foo(); //window
      
      var a = {
          b: function() {
              var c = function() {
                  return this;
              };
              return c();
          }
      };
      
      a.b(); //window
    3. 透過 Function.prototype.call 執行
      this 根據傳入的參數決定
    4. 透過 Function.prototype.apply 執行
      this 根據傳入的參數決定
      var a = {
          b: function() {
              return this;
          }
      };
      
      var d = {};
      
      a.b.apply(d); //d
    5. 透過 new 執行 constructor
      this 等於新生成的物件
      var A = function() {
          this.toString = function(){return "I'm an A"};
      };
      
      new A(); //"I'm an A"
  3. Evaluation context
    this 的值跟呼叫所在的 this 同義
    alert(eval('this==window')); //true - (except firebug, see above)
    
    var a = {
        b: function() {
            eval('alert(this==a)');
        }
    };
    
    a.b(); //true;

Related Posts Plugin for WordPress, Blogger...