2013年5月29日 星期三

[C#]?? Operator (null coalescing operator)

?? operator可以被用在nullable types以及reference types
語法的意思很簡單:如果operand不是null,把他回傳給我;否則給我一個預設的值。比如說以下例子因為x是null所以y被預設成5。
int? x = null;
int y = x ?? 5;  // y is 5
first ?? second語法被執行的步驟如下:

  1. 檢查first 
  2. 如果first不是null,回傳first 
  3. 如果first是null,回傳second

?? operator可以用來串接多個nullable variable以及reference variable。會回傳第一個不是null的變數。以下範例會印出1,因為a是null。
int? a = null, b = 1, c = 2;
Console.WriteLine (a ?? b ?? c);  // 1
善用?? operator可以讓我們的程式碼變得更簡潔,以下是一個範例程式用來計算使用者的年紀,這個例子沒有使用?? operator。
DateTime birth;
DateTime? death;
public TimeSpan Age
{
    get
    {
        if (death == null)
        {
            return DateTime.Now - birth;
        }
        else
        {
            return death.Value - birth;
        }
    }
}
使用?? operator我們可以把上面的範例改寫成好懂又精簡的版本。
DateTime birth;
DateTime? death;
public TimeSpan Age
{
    get
    {
        DateTime lastAlive = death ?? DateTime.Now;
        return lastAlive – birth;
    }
}

[C#]Optional parameters and Named arguments

C#從4.0版之後開始支援optional parameters以及named arguments,這兩個語法通常會一起使用。他讓我們的程式碼變得更簡潔並增加可讀性。首先先來介紹什麼是Parameters and Arguments。

Parameters and Arguments

Parameters和Arguments是兩個很容易搞混的名詞,先來定義這兩者有什麼不同。下面是一段程式碼範例:

void Foo(int x, int y)
{
// Do something with x and y
}
...
int a = 10;
Foo(a, 20);

parameters是function定義時所使用的變數,arguments是我們呼叫function時傳進去的變數或值 範例中的x和y是parameters,a和20是arguments。

Optional parameters

Optional parameters簡單說就是給parameter一個特定的預設值,當使用者沒有傳入此parameter所對應的arguments時,會自動使用預設值。在C#4.0之前沒有optional parameters,如果要達到這個功能必須使用overloading function,會造成寫一大堆overloading function的情況,並不是很好使用。C#4.0之後使用optional parameters可以免去宣告一堆overloading function的困擾。以下是optional parameters的一個簡單範例:

static void Dump(int x, int y = 20, int z = 30)
{
    Console.WriteLine("x={0} y={1} z={2}", x, y, z);
}
...
Dump(1, 2, 3);
Dump(1, 2);
Dump(1);

我們設定y和z的預設值為20和30,當我們在呼叫function時,編譯器會根據我們提供的參數去做對應,找出最合適的function。程式執行結果如下:

x=1 y=2 z=3
x=1 y=2 z=30
x=1 y=20 z=30

程式會從parameters list的左邊到右邊一一去對應參數。如果出現無法對應的情況會丟出exception。

由於optional parameters和arguments的對應是按照宣告的順序,因此當我們呼叫Dump(1, 2)時會自動把y對應成2,沒辦法把2對應到z讓y使用預設值。要讓參數能自由對應需要使用接下來要介紹的named arguments。

parameter的預設值必須是一個常數,因此以下範例是錯誤的,因為DataTime.Now並不是一個常數。

Foo(DateTime dt = DateTime.Now)

Optional parameters也被限制必須放在後面,不能放在非optional parameters之前。

Foo(int x = 0, int y)

Name Arguments

Name arguments讓我們可以不按照順序傳入參數。他的語法是在argument前加入一個對應parameter的名稱。以下是範例:

static void Dump(int x, int y, int z)
{
    Console.WriteLine("x={0} y={1} z={2}", x, y, z);
}
...
Dump(1, 2, 3);
Dump(x: 1, y: 2, z: 3);
Dump(z: 3, y: 2, x: 1);
Dump(1, y: 2, z: 3);
Dump(1, z: 3, y: 2);

不管argument的順序是如何,編譯器會自動幫我們對應到正確的parameters名稱。

x=1 y=2 z=3
x=1 y=2 z=3
x=1 y=2 z=3
x=1 y=2 z=3
x=1 y=2 z=3

Abusing argument evaluation order

int i = 0;
Dump(z: ++i, x: ++i, y: ++i);
x=2 y=3 z=1

上面這行程式碼輸出的結果可能會和你想的不一樣,這是因為傳進去的argument還是根據他宣告的順序做計算,而不是patameters的順序。我們應該要避免寫出這種程式碼。

2013年5月23日 星期四

C# in Depth(精通C#)

這是一本想更深入瞭解C#的人必讀的一本書。作者完整的交代了C#1.0到C#4.0之間的改變,這樣的寫法讓讀這本書有點像在念C#的歷史。書中詳細的說明C#每次改版新增了哪些功能,以及為什麼要新增這些功能,是為了解決什麼樣的問題。

我一直覺得要瞭解一個程式語言的語法最重要的事情就是瞭解語法演進的過程。其實就像學習數學和物理一樣,只會死背公式是沒有用的。程式語言作者設計某種語法一定是有他的原因。如果我們不懂背後設計的原由,就很難活用這個語法。學習程式語言另一個重要事情是此語法實際的用途和例子,比如說使用某種語法可以改善程式的可讀性,讓程式看起來更精簡。這兩點作者都有完整的解釋,這也是這本書為什麼被這麼多人推薦的原因。

書中甚至提到一些compiler相關的東西,像是compiler如何推論匿名型別的正確型別,LINQ和extension methods是如何被compiler轉譯。這些東西在其他C#的書幾乎沒有被解釋。雖然說不用懂這些也能寫程式,不過我相信要寫出好程式多瞭解這些東西是會有幫助的。

這本書雖然有中文版,但是我覺得翻譯不是很理想。在閱讀過程中常常可以看到錯字或缺字,句子翻譯起來不太通順(書中很常出現"然而"有點妨礙閱讀),甚至會有少翻的情況發生。因此還是建議有能力的讀者可以閱讀原文本。另外第二版沒有中文版是蠻可惜的,中文版只有介紹到C#3.0。

總而言之,這是一本C#的經典書籍,很適合學完基本語法後當C#的第二本書,看完之後一定會對C#有更深入的體認

最近看到C# in Depth要出第三版,內容涵蓋了C#5.0的語法。有興趣的人可以到Amazon網站注意出版訊息。

2013年5月20日 星期一

在Visual Studio 2012的C++ Projects中設定target framework

Visual Studio 2012沒有UI可以設定C++ Project的Target Framework
唯一的方法就是去修改.vcxproj裡頭的TargetFrameworkVersion
可以用常用的文字編輯器直接把.vcxproj打開,直接修改TargetFrameworkVersion就可以了
C++ project新建立時候預設會使用v4.0,在.vcxproj裡頭有可能會找不到TargetFrameworkVersion
這時候可以自己加入TargetFrameworkVersion tag到PropertyGroup Globals中
下面是一個範例,此設定檔的Target Framework是v4.5
<PropertyGroup Label="Globals">
      ...
      <RootNamespace>...</RootNamespace>
      <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>

2013年5月7日 星期二

[Python]如何disassemble Python程式碼

大家都知道Python程式碼會被編譯成Python bytecode並執行在Python的VM上。有沒有什麼方法可以把Python程式碼變成bytecode印出來呢?Python有個disassembler module可以幫助你把Python code轉成bytecode。
以下是一段範例。
>>> import dis
>>> def foo():
...     a = 1
...     b = 2
...     c = a + b
...
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 (1)
              3 STORE_FAST               0 (a)

  3           6 LOAD_CONST               2 (2)
              9 STORE_FAST               1 (b)

  4          12 LOAD_FAST                0 (a)
             15 LOAD_FAST                1 (b)
             18 BINARY_ADD
             19 STORE_FAST               2 (c)
             22 LOAD_CONST               0 (None)
             25 RETURN_VALUE
dis module中的dis method可以幫助你把Python code轉成byte code

2013年5月3日 星期五

[C]#if和#else,切換程式碼的小技巧

在寫程式時當我們修改程式碼時通常會把舊有的程式碼註解起來,再加入新的程式碼。有沒有什麼方法能快速在新舊程式碼之間切換呢?C裡頭的#if和#else是一個好方法。下列是一個常見的新舊程式碼範例,我們用註解把程式碼分開:
                   
newCode();             
/*                     
someOtherCode();        
*/
/*  
newCode();             
*/                      
someOtherCode(); 
其實可以用C裡頭preprocessor的小技巧來快速切換程式碼
#if 1
newCode();  
#else
test(); /* FIXME: please don't to that. */
someOtherCode(); 
#endif    
#if 0
newCode();
#else
test(); /* FIXME: please don't to that. */
someOtherCode(); 
#endif    
如此一來我們只要切換1和0就可以快速切換不同區塊的程式碼,就算裡頭有註解也不用擔心
要註解一大塊程式碼也可以使用這個技巧
#if 0
newCode();
test();
#endid

2013年4月10日 星期三

Static Typing, Dynamic Typing, Strong Typing, Weak Typing

Static Typing

如果一個程式語言中的每個變數型別在編譯時期就完全決定好,我們稱這個程式語言屬於static type。這類的程式語言在使用變數時通常都需要先宣告變數,編譯器會在編譯時期去做type checking。比如說Java, C, C++, C#。以下是一段C的範例:

int num, sum; // explicit declaration
num = 5; // now use the variables
sum = 10;
sum = sum + num;

變數使用前需要宣告是static type的特徵之一

Dynamic Typing

Dynamic type和static type相反,一個屬於dynamic type的程式語言不需要宣告變數,因此編譯器無法對型別做檢查在編譯時找出型別上的錯誤。在執行期間存取變數時,會透過適當的機制來判斷此變數可能的型別。Dynamic type不需要事先宣告變數就能直接使用,比如說Python, PHP, Ruby, Perl。以下是一段Python的範例程式:

sum = 1   // directly using the variable
sum += 2

上面的範例我們直接使用變數sum而沒有事先宣告,這是dynamic type的其中一個特徵。 簡單來說,Static typing會在編譯時去做type checking。而dynamic type會在run-time時做check。

Strong Typing

Strong typing很常會和static typing以及dynamic typing搞混。Strong typing是指變數會被綁定到某個型別,除非經過明顯的型別轉換,不然型別不會任意改變。比如說Python就是strong typing,以下是個範例:

>>> x = 3
>>> y = '4'
>>> print(x+y)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Python不允許int和string相加,除非我們主動去做型別轉換,不然x和y變數不會經過隱藏的型別轉換加在一起。這就是Strong typing的主要特徵。Python是dynamic typing同時也是strong typing

Weak Typing

Weak typing是指型別的概念比較弱,變數的型別可能隨時會經過隱性的轉換。比如說JavaScript,以下是JavaScript的範例:

var x = 3    
var y = '4'
alert(x + y) //Produces "34"

不同於Python,JavaScript允許這種語法並且會自動幫你做型別轉換。這就是weak typing的特徵之一。

Strong Typing v.s. Weak Typing

其實這兩者並沒有一個約定成俗的定義,很多程式語言都同時有兩種的特性。只能說偏向某一邊比較多。比如說C,大部分人認為C偏向Weak Typing,因為C有pointer可以做任意轉型,同時也有void *這種變數可存取任意型別。

2013年4月9日 星期二

Type-safe v.s. Type-Unsafe

某一些程式語言比較自由,允許我們去做一些危險的動作。比如說C和C++的Pointer就允許我們直接對記憶體做操作。這類的功能讓程式語言變得很強大,但是一不小心有可能會讓程式整個當掉。這種允許我們任意存取記憶體位置的程式語言就不能算是Type-safe。比如說以下的C程式讓我們access超過int boundary的位置:
int i = 10;
char *s = (char*)i;
print(*(s+10));
這種問題不會發生在C#身上,因為C#是Type-safe的程式語言。以下範例在C和C++合法,但是在C#會丟出錯誤訊息:
double d = 10.59; 
int i = 5; 
i = d; //this causes an Error
在C#中不能隨便把某個型別的資料當作是另外一種型別,如果編譯器認為這樣轉型是不合法的,在編譯的時候就會丟出錯誤。

[C#]Delegate

如果你有學過C,C#的delegate其實很類似C的function pointer。delegate讓我們可以把function透過參數的形式來傳遞,也就是說delegate可以讓我們把function當作first class opject來操作。下列是一個delegate的簡單範例:
using System;
delegate void StringProcessor(string input);
class Person
{
    string name;
    public Person(string name) { this.name = name; }
    public void Say(string message)
    {
        Console.WriteLine("{0} says: {1}", name, message);
    }
}

class SimpleDelegateUse
{
    static void Main()
    {
        Person jon = new Person("Jon");
        Person tom = new Person("Tom");
        StringProcessor jonsVoice, tomsVoice;
        jonsVoice = new StringProcessor(jon.Say);
        tomsVoice = new StringProcessor(tom.Say);
        jonsVoice("Hello, son.");
        tomsVoice.Invoke("Hello, Daddy!");
    }
}
此範例中我們建立了宣告了兩個delegate的instance,分別是jonsVoice以及tomsVoice。最後用兩種不同的方式呼叫function
使用delegate要有以下三個步驟:

1. 宣告delegate type
delegate void StringProcessor(string input);
這行就是我們宣告delegate type的地方,這個delegate的type是含有一個string參數且回傳值為void的function。此delegate type只能接受回傳值以及參數個數一模一樣的function。不過有一種特殊情況:
void Test(object x);
此function也可以被assigned給此delegate的instance,因為string是繼承自object

2. 建立delegate instance
StringProcessor jonsVoice, tomsVoice;
jonsVoice = new StringProcessor(jon.Say);
用new可以建立剛才宣告好得delegate instance,建立傳入的參數是之後要透過delegate呼叫的function

3. 呼叫delegate instance的function

編譯之後會把jonsVoice("Hello, son.")轉成jonsVoice.Invoke("Hello, son.")
最後又會透過delegate呼叫原本assigned的function jon.Say("Hello, son.")

delegate很常用在UI Button的click,我們會事先宣告EventHandler的delegate instance。當button被按下時,執行對應的處理function。有點類似call back function。

每個delegate instance內部都有一個invocation list用來紀錄function,也就是說一個delegate instance可以紀錄不只一個function。當有多個function時,delegate會一一呼叫invocation list中所儲存的function。我們可以透過+=以及-=運算子來新增移除function。以下是個簡單的範例:

void HowAreYou(string sender) {
  Console.WriteLine("How are you, " + sender + '?');
}
 
void HowAreYouToday(string sender) {
  Console.WriteLine("How are you today, " + sender + '?');
}
 
Notifier greetMe;
 
greetMe = new Notifier(HowAreYou);
greetMe += new Notifier(HowAreYouToday);
 
greetMe("Leonardo");                      // "How are you, Leonardo?"
                                          // "How are you today, Leonardo?"
 
greetMe -= new Notifier(HowAreYou);
 
greetMe("Pereira");                       // "How are you today, Pereira?"

2013年4月3日 星期三

[C#]快速讀取、寫入檔案的方法

C#提供一些快速的Method可以直接寫入或是讀取檔案,善用的話可以節省一些時間
以下三個static methods只需要一個步驟就可把資料從檔案讀到記憶體中,傳入參數都是讀取檔案的路徑
File.ReadAllText(path)                        // (回傳string)
File.ReadAllLines(path)                       // (回傳array of strings)
File.ReadAllBytes(path)                       // (回傳一個byte array)

下列四個static methods同樣只需要一個步驟就可把資料寫入到檔案中,傳入參數是路徑以及寫入的資料
File.WriteAllText(path, string)
File.WriteAllLines(path, array of strings)
File.WriteAllBytes(path, bytes)      
File.AppendAllText(path, string)              // (適合用在log file)
File.ReadAllLines和File.ReadLines看起來很像,但是ReadAllLines會一次把所有資料讀到記憶體中
ReadLines會回傳一個IEnumerable<string>,不會一次把所有資料讀到記憶體中

2013年4月2日 星期二

如何讓Visual Studio 2012中compile的程式可在Windows XP上執行

最近發現一個問題,Visual Studio 2012預設並不支援Windows XP。也就是說Compile出來的程式沒辦法在XP上執行。花了一點時間上網找解決方法,發現微軟在去年年底出的Visual Studio 2012 Update 1已經可以支援XP了。程式開發人員可以在Visual Studio 2012上撰寫程式並compile成XP可執行的執行檔。

安裝好更新檔之後還需要設定專案所使用的toolset為Visual Studio 2012 – Windows XP (v110_xp)

詳細步驟如下:

1. 在Solution Explorer中的Project上按右鍵並點選Properties

2. 在左手邊樹狀目錄中選取Configuration Properties, General,把Platform Toolset property設定成Visual Studio 2012 – Windows XP (v110_xp)

3. 重新編譯後會產生可在XP上執行的執行檔

2013年3月28日 星期四

無瑕的程式碼-敏捷軟體開發技巧守則 (Clean Code: A Handbook of Agile Software Craftsmanship)


最近發現Clean Code總算出中文翻譯書了,去書局翻了一下內容發現翻譯品質還不錯。害怕原文書的朋友們可以考慮買這本翻譯書。前陣子也看到O'reilly最近要出The Art of Readable Code的中文翻譯本,這本書和Clean Code主題類似,有興趣的人可以注意一下出版的消息。

很高興看到有越來越多好書被翻譯成中文,也希望未來能夠有更多經典書能被翻譯成中文讓更多人閱讀。

原文板的讀書心得和筆記如下:

心得 : Clean Code

筆記 :

寫入資料到txt檔,用Notepad打開顯示亂碼

今天碰到一個奇怪的問題,我有一個程式會產生一個.txt檔。內容為上千個0~255的數字由空白隔開。寫檔成功後在Windows XP底下用Notepad打開發現顯示為亂碼,但是用Notepad++以及Wordpad打開都可正常顯示。很明顯這是編碼的問題,不過為什麼會這樣讓我找了一陣子。最後發現是Notepad的問題。

Windows XP的Notepad有Unicode Detection的功能,Notepad使用一個Windows API叫IsTextUnicode()來偵測目前文字檔是不是Unicode編碼。但是這個API並沒辦法完美的辨認出Unicode,有時後會把ASCII text誤認為是Unicode編碼。比如說這段字串"aaaa aaa aaa aaaaa",如果你把他貼在Notepad上存起來再重新打開,你會發現他變成一團亂碼。原因就在於IsTextUnicode()把全部都是此ASCII text誤認為UTF-16。除了此case以外還有很多其他的case會造成誤判。而Wordpad以及Notepad++不會有此誤判的現象,因此可正常顯示。

2013年2月5日 星期二

Coding Style Guide

以下網址是google以及llvm所制定的C++ coding standars,提供給想改善程式碼品質的人參考看看

LLVM Coding Standards
http://llvm.org/docs/CodingStandards.html#include-style

Google C++ Style Guide
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml


Google同時還有制定Python Coding Style Guide
Google Python Style Guide
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html

Python Code Style
http://docs.python-guide.org/en/latest/writing/style/

PEP 8 -- Style Guide for Python Code
http://www.python.org/dev/peps/pep-0008/

2013年1月20日 星期日

Peopleware:腦力密集產業的人才管理之道 (Peopleware: Productive Projects and Teams, 2/e)

就如同封面所寫,這是一本軟體專案管理的聖經本,和人月神話齊名。雖然這是一本26年前出版的書籍,但是書中觀念現在同樣還是適用。甚至現在還有許多軟體公司使用書中所寫得錯誤方法來管理員工。

書中一開始就講到,軟體業和一般製造業完全不同。軟體業比較像是藝術創作。老闆如果把員工當成可替換的零件,那這家公司註定是要失敗的。Peopleware提到幾個成功的關鍵,例如管理底下的軟體工程師的方式會決定這家公司會成功還是失敗。書中提了許多實際案例以及有趣的實驗,一一破解傳統主管的迷思。舉例來說,書中提到強迫員工加班的成效並沒有一般人想的這麼大,長久下來反而會造成員工效率低落。書中也提到環境對員工的影響,優秀的程式設計師大多數都來自於安靜不易受干擾的工作環境,但是老闆往往為了省小錢而造成工程師效率低落。

我最喜歡書中做的一個實驗,作者把不同公司的工程師們安排在自己的工作環境進行程式競賽。結果發現最優秀的工程師和最差的工程師效率可以差到10倍。同時最優秀的那群工程師們大多數擁有良好的工作環境。

Peopleware這本書不只適合給主管閱讀,也很推薦一般工程師去閱讀這本書。書中提到許多改善工作效率的方法。讀者可一一檢視,改善自己的工作效率。

2013年1月10日 星期四

[C]函式指標陣列用法(Array of Function Pointers)

在寫程式時候,我們常常會碰到根據不同狀態去執行不同函式的情況。比如說下列範例,根據state變數的狀態去呼叫對應的函式。

state: 0,執行run()
state: 1,執行stop()
state: 2,執行exit()

void run() {
    printf("start\r\n");
}

void stop() {
    printf("stop\r\n");
}

void exit() {
    printf("exit\r\n");
}
bool OnStateChange(uint state) {
    if (state == 0) {
        run();
    }
    else if (state == 1) {
        stop();
    }
    else if (state == 2) {
        exit();
    }
    else {
        printf("Wrong state!\n");
        return false;
    }       

    return true;
}
用if判斷式是最簡單的方法。不過如果state變多。整段code就會變得很冗長。
int OnStateChange(uint state) {
    switch (state) {
    case 0:
        run();
        break;
    case 1:
        stop();
        break;
    case 2:
        exit();
        break;
    default:
        printf("Wrong state!\n");
        return false;
    }
    return 0;
}
改用switch看起來有比較乾淨一點。不過state變多,程式碼也會變得冗長。有沒有什麼更精簡的作法呢?這時候函式指標陣列就派上用場了。
static void (*command[])(void) = {run, stop, exit};

int OnStateChange(uint state) {
       
    if (state > 3) { 
        printf("Wrong state!\n");
        return false;
    }

    command[state]();
    return 0;
}
這種寫法比前兩個例子都來的精簡。函式指標陣列宣告的第一個void表示函式的回傳值,第二個void表示函式的參數。所以這是一個帶有三個函式指標的陣列,陣列中函式的回傳值以及參數都是void。

2013年1月7日 星期一

The Art of Readable Code



這是一本教你如何寫出容易閱讀程式碼的書。其實市面上已經有許多這類型的書籍了,比如說Clean Code。如果你是一位很有經驗的程式設計師,你會發現這本書內容其實和之前市面上這類型的書籍都差不多,並沒有什麼新的東西。這本書作者很強調一個觀念,好讀的程式碼是指讓別人花最少時間就可以完全瞭解它,整本書就是圍繞這個原則來教你如何寫出容易閱讀程式碼。

這本書最大的特色是例子很豐富,書中舉的例子包涵了C, C++, Java, Python, JaveScript。其中大多數的範例都是作者花超過五年的時間從實際專案中的程式碼擷取下來的,看得出來作者花了很多時間在收集這些範例。書中會先解釋舊的程式碼為什麼不好,並且教讀者該如何修改成好的程式碼。

另一個本書的特色是書中的漫畫,書中每個章節都會有一些漫畫。而這些漫畫都和所談得主題有些呼應,讓讀者讀起來相當輕鬆。本書頁數也不長,只有180頁。這樣的篇幅我覺得十分洽當,不會太長也不至於到沒有內容。

書中內容專注在如何寫出別人容易閱讀的程式碼,從命名、註解一直到程式邏輯,最後教你如何重新精簡你的程式碼以及如何測試。重要的主題都有涵蓋在裡頭。書中最後也列出作者推薦相關類型的書籍。讓讀者在讀完這本書後能繼續深入閱讀。

很推薦想增加自己程式碼品質的人去閱讀這本書。如果之前沒讀過這類型得書籍,相信收穫會很多。

讀書筆記:

2013年1月4日 星期五

如何命名變數以及函式名稱

  • 命名要精準

    • 命名要精準,不要使用模糊的單字
    • def GetPage(url):
      
    • Get是一個不好得命名,沒有明確指出從什麼地方GetPage
    • 如果是從Internet,應命名為FetchPage()或DownloadPage()
    • class BinaryTree:
          def Size(self):
              ...
      
    • Size是一個不好得命名,沒有明確指出是tree的高度還是有多少個nodes
    • 直接命名為Height()或是NumNodes()會比較明確

  • 找尋更能表達意義的單字

    • 使用字典找尋適合的單字
    • 以下是一些colorful word的範例,=>左手邊的單字可用右手邊的單字取代
    • send => deliver, dispatch, announce, distribute, route
    • find => search, extract, locate, recover
    • start => launch, create, begin, open
    • make => create, set up, build, generate, compose, add, new

  • 避免tmp和retval這類通用的命名

    • 讀者沒辦法從retval中獲得更多資訊,使用更清楚的命名取代retval
    • tmp只適合用在很短生存範圍的程式碼,例如下列swapping範例tmp生存範圍只在if{}內
    • if (right < left) {
          tmp = right;
          right = left;
          left = tmp;
      }
      

  • Loop Iterator

    • iterator的名稱使用i, j, k是OK的,因為這是大家都習慣的用法
    • for (int i = 0; i < clubs.size(); i++)
          for (int j = 0; j < clubs[i].members.size(); j++)
              for (int k = 0; k < users.size(); k++)
                  if (clubs[i].members[k] == users[j])
                      cout << "user[" << j << "] is in club[" << i << "]" << endl;
      }
      
    • 最好在i, j, k前加入prefix的字串,用來辨別iterator所走訪的物件
    • if (clubs[ci].members[mi] == users[ui])
      
    • 如此一來有bug就能即時發現
    • if (clubs[ci].members[ui] == users[mi]) # Bug! First letters don't match up.
      

  • 數值和單位

    • 在命名數值變數時可以加上單位
    • Start(int delay); 
      CreateCache(int size); 
      ThrottleDownload(float limit); 
      Rotate(float angle); 
      
      Start(int delay_secs); 
      CreateCache(int size_mb);
      ThrottleDownload(float max_kbps); 
      Rotate(float degrees_cw); 
      
    • 第二個範例會比第一個範例好

  • 命名要包含資料狀態

    • 加入目前資料的狀態到命名
    • password : plaintext_password
    • comment : unescaped_comment
    • html : html_utf8
    • data : data_urlenc
  • 在短生存範圍內可使用簡短的命名

    • 變數m只出現在if的範圍內,不影響其他人理解這段程式碼
    • if (debug) {
          map<string,int> m;
          LookUpNamesNumbers(&m);
          Print(m);
      }
      

  • 不使用不常見的縮寫

    • 用BEManager取代BackEndManager並不是一個好得命名
    • 團隊新來的成員沒辦法搞懂這些縮寫

  • 刪除多餘的命名

    • ConvertToString() => ToString()
    • DoServeLoop() => ServeLoop()

  • 使用命名格式來區分不同意義

    • const, macro, class, private variable都有不同的命名格式,方便一眼區分
    • static const int kMaxOpenFiles = 100;
      class LogReader {
        public:
          void OpenFile(string local_file);
        private:
          int offset_;
          DISALLOW_COPY_AND_ASSIGN(LogReader);
      };
      

    2013年1月3日 星期四

    撰寫良好程式碼最重要的原則


  • 程式碼應該要容易被理解

    • 程式碼應該要容易被理解,讓別人花最少時間讀懂程式碼
    • 易讀的程式碼同事意味著有良好的架構且較容易測試
    • 大多數時候,攥寫較短的程式碼會比長的程式碼來得好(但是也有例外)



  • 撰寫較少的程式碼通常比較好

    • 2000行程式碼會比5000行程式碼來的容易瞭解
    • 某些情況長的程式碼會比短的程式碼來得容易瞭解
    • assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());
      
    • 第二個例子雖然比較長,但是比第一個例子容易理解
      bucket = FindBucket(key);
      if (bucket != NULL) assert(!bucket->IsOccupied());
      
    • 加上一些註解會更容易瞭解
      // Fast version of "hash = (65599 * hash) + c"
      hash = (hash << 6) + (hash << 16) - hash + c;
      
    • 永遠都以撰寫好讀的程式碼為目標

    2013年1月2日 星期三

    [Python]os.rename()修改檔案檔名

    os module中的rename()可以幫助我們修改檔案或是資料夾的檔名,以下是一個簡單的範例簡介如何使用os.rename來更改檔名。範例中把path底下的所有檔案從0開始依序編號更改檔名。此範例使用os.listdir()來列出目錄下的所有檔案名稱,以及使用os.path.join()來產生路徑。

    os.rename(sourece, dest) - 更改資料夾或檔案的名稱(從source改成dest)
    os.listdir(dir_path) - 回傳一個list包涵dir_path資料夾中的所有檔案名稱
    import os
    
    def batch_rename(path):
        count = 0
        for fname in os.listdir(path):
            new_fname = str(count)
            print os.path.join(path, fname)
            os.rename(os.path.join(path, fname), os.path.join(path, new_fname))
            count = count + 1