2011年2月28日 星期一

[php] 程式寫作技巧 - 查表法

最早接觸到查表法這個名詞,是在大二的時候上侯老 (侯廷偉教授) 的作業系統課程,課程中侯老偶然提到:程式寫作時有個重要技巧叫做查表法,它是透過建立索引的方式 來帶出相對應的資料。比如:想要查詢一個人的電話號碼時,我們並不需要真的記住那個號碼,只需要記住那個人的名字即可。我們所要做的,就是拿出手機,並且 在手機的通訊錄內找到那人的名字,接著進入查閱即可。作業系統中很多功能都是採用查表法的方式來處理,比如說檔案系統的檔案資料索引,就是透過建立一個超 大的 index table 來紀錄檔案的在硬碟中的真實位置。

寫了多年的程式,接觸過 C/C++JAVAassemblyHTMLPHPMATLABVBJSPJSTLC#...等等。每種語言有他的應用特性,C有執行效能上優勢、JAVA有跨平台的優勢 (雖然我覺得更重要的是他的VM概念)、assembly的應用程式有最精簡且快速的特性、MATLAB提供相當多數學以及模擬的運算功能、C#結合了VB的寫作介面概念以及類似C(C++)語言的結構、PHP的容易撰寫...等等。我曾寫過很多有趣的應用程式,每個應用程式的開發過程是在完成功能之後,接著才會去要求美觀以及效能。
其中,完成要求的功能其實是最基本也最容易的。不管什麼語言,只要在接觸個半年之後就能夠參考前人的作法寫出所要求的功能。例如要在 console 下用C弄個跑馬燈程式,只要查到部份關鍵功能寫作方式,接著就能夠按照個人寫作技巧來完成。有些人會用拼拼湊湊法,把所有該印出來的訊息在原始碼內一行行的排列;有些人會善用 function 把會重複出現的訊息用函數包起來,接著重複使用以節省開發時間以及達到原始碼簡潔的效果;有些人會進一步運用變數陣列,透過精密分析過的演算法與查表法來完成。每一種方法都有優點與缺點,查表法的優點就是快速索引

到目前為止,我仍然偏好使用PHP來設計應用程式,最大的原因就是它的程式開發便利性弱資料型別、豐富的函式庫、多年的經驗、累積大量的樣本程式是主要原因。老祖先有在講:"工欲善其事,必先利其器"。大量的樣本程式、多年的經驗、豐富的函式庫提供了使用工具上的優良條件,雖然到目前為止我在開發PHP應用程式的時候依然使用單純的文字編輯器 (editPlus) ,不過靠著使用工具上的便利,在開發的速度上還算OK。

那剩下的弱資料型別查表法有啥關係?有寫過弱資料型別類型語言的人都知道,所謂的弱資料型別就是變數型態不需要特別宣告,餵變數一個常數它攜帶的內容就是數字,餵變數一個字串它攜帶的內容就是字串,而在各種型態資料的整合上也很自由。透過運算符號的協調,$c = $a + $b; 以及$c = $a . $b; 分別代表數字的相加以及字串的連接運算。由於PHP也是弱資料型別,接著就以PHP為主軸來介紹。

舉一個最簡單的性別訊息索引:
  $TABLE = array ( "0" => "女", "1" => "男");
  $gender = $TABLE[ $gender_index ]; // $gender_index, 0:女, 1:男

或者是查詢月份:
  $TABLE = array ( "1" => "一月", "2" => "二月", "3" => "三月", "4" => "四月", "5" => "五月", "6" => "六月", "7" => "七月", "8" => "八月", "9" => "九月", "10" => "十月", "11" => "十一月", "12" => "十二月");
  $month = $TABLE[ $month_index ];



當然,這些基本的東西隨便翻翻PHP的教學文章都有,所以來點不一樣的。PHP常常跟MySQL做結合,所以一個DB Query之後的結果,也會是一個表格,一樣可以透過PHP處理:
  $query = "select * from table_account"; // query 語法
  $getRecord = getQueryFields($query); // 自訂函數(註一),向DB進行查詢,查詢結果置於   $getRecord

  echo $getRecord[ 0 ][ user_name ];       // 顯示第一筆資料的使用者名稱
  echo $getRecord[ 1 ][ email ];                 // 顯示第二筆資料的使用者email

當然,其中的 user_name 以及 email 則是對應於DB table 裡所定義的屬性 (attribute/field)



習慣了基本的查表法之後,接著更進階一點的概念就是把陣列當成物件。如同上一個範例中可以看到:$getRecord 裡,每一筆資料都可以看作一個使用者的帳號 (物件)。而 $getRecord 本身也可以看作是符合條件的使用者資料 (物件)。

而物件之間很多時間需要結合,所以,我們可以用查表法的方式來把多個物件組合在一起。例如,我想要查詢使用者帳號以及消費資料:
  $query = "select * from table_account";                      // query 語法
  $getAccount = getQueryFields($query);                     // 取得使用者帳號
  $num_getAccount = count( $getAccount );                 // 取得資料筆數
  for($i=0;$i<$num_getAccount;$i++)
    $TABLE_account[ $getAccount[$i][user_id] ] = $i; // 建立索引

  $query = "select * from table_consume group by user_id"; // query 語法,稍微整理一下
  $getConsume = getQueryFields($query); // 自訂函數,向DB進行查詢,查詢結果置於  $getRecord
  $num_getConsume = count( $getConsume );          // 取得資料筆數
  for($i=0;$i<$num_getConsume;$i++){
    $index = $TABLE_account[ $getConsume[$i][user_id] ]; // 得到索引
    $getAccount[ $index ][ consume ] = $getConsume[$i];     // 把消費資料嵌入使用者帳號
  }

如同範例,用查表法的方式就可以輕易的把兩個關聯的物件結合起來。



最後,提供一個我最近常用的查表法應用:彈性的使用DB table。在使用資料庫的時候,很多時候需要根據要求來進行擴充、修改 DB table。然而,學過 MVC (Model/ View/ Control) 網頁設計架構的人都知道,DB的 Model 是網頁設計最基礎的地方,在這裡需要定義各種資料的關聯,建立好所有 table,接著才是去實作網頁程式。所以修改了 Model ,所實作的網頁程式很容易有大地震。

所以有一個比較彈性的作法來避免DB table 的修改。操作方式為:
1. 在該 table 中建立一個額外處醒 Ex: comment,並設定資料型態為Text。 (注意,此方法所處理的資料無法直接藉由DB進行索引)
2. 把想加入的資料包成一個物件。
3. 我採用了 json 這個資料結構方式來打包物件成字串。 (該架構如同 xml)
4. 將該筆資料存入DB

  $data = json_encode($data); // json編碼化
  $query = "insert into table_account ( consume ) values ( '".$data."') ";
  mysql_query($query);

而提出資料的方式則是 2.~4.反過來的步驟。

  $query = "select consume from table_account"; // query 語法
  $getConsume = getQueryFields($query); // 取得使用者帳號
  $getAccount[ consume ] = json_decode( $getConsume[0][ consume ], true);




[ 註解 ]
註一: DB query 函數細節
function getQueryFields($query)
{
  $result = mysql_query($query);
  $rows = mysql_num_rows($result);
  for($i=0;$i<$rows;$i++)
    $data[$i] = mysql_fetch_array($result);
  return $data;
}

沒有留言: