<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      表驅動法 -《代碼大全》讀書筆記

      表驅動法是一種編程模式,從表里面查找信息而不是使用邏輯語句(if…else…switch),當是很簡單的情況時,用邏輯語句很簡單,但如果邏輯很復雜,再使用邏輯語句就很麻煩了。

      比如查找一年中每個月份的天數,如果用表驅動法,完全不需要寫一堆if…else…語句,直接把每個月份的天數存到一個數組里就行了,取值的時候直接下標訪問,最多針對二月判斷一下閏年。這么算的話,平時用的的HashMap,SparseArray也可以算是表驅動

      表里可以存數據,也可以存指令,或函數指針等都可以。

      示例

      看一個例子,計算保險的保險費率,不同年齡的人費率是不同的,當判斷費率時就要寫很長的if…else…來判斷不同的年齡段對應的費率,如果要區分性別,那么判斷語句就會增加一倍,如果再判斷是否吸煙,是否已結婚,每一個條件都會使判斷增加一倍。雖然可以通過給是否已結婚,是否吸煙等設置一個比例系數,這樣就不用使判斷加倍,但這個系數可不能保證對不同年齡段或不同性別的人是相同的,而且如果要改變,就要很麻煩地改程序。

      如果用表驅動法解決這個問題,直接拋棄邏輯判斷,使用一個表保存各個條件的費率,比如只考慮是性別,是否吸煙和年齡,就可以定義一個三維數組保存各個條件的費率

      double[][][] rate = {
              {{1,2,3}, {1,2,3}},
              {{1,2,3}, {1,2,3}}
      };
      
      for (int gender = 0; gender < rate.length; gender++) {
          System.out.println("Gender:" + gender);
          double[][] genderRate = rate[gender];
          for (int smoke = 0; smoke < genderRate.length; smoke++) {
              System.out.println("\tSmoke:" + smoke);
              double[] ageRate = genderRate[smoke];
              System.out.print("\t\tAge:");
              for (int age = 0; age < ageRate.length; age++) {
                  System.out.print(ageRate[age] + " ");
              }
              System.out.println();
          }
      }
      // Output
      Gender:0
          Smoke:0
              Age:1.0 2.0 3.0 
          Smoke:1
              Age:1.0 2.0 3.0 
      Gender:1
          Smoke:0
              Age:1.0 2.0 3.0 
          Smoke:1
              Age:1.0 2.0 3.0
      當想取一種情況的費率時直接訪問數組就行了,比如封裝成下面的方法
      private static double getRate(int gender, int smoke, int age) {
          return rate[gender][smoke][age];
      }
      前兩個條件性別和是否吸煙還好說,條件還是相對固定的,對于年齡,不可能在數組中針對每個年齡定義一個數據,因為一般都是按年齡段分的,這種情況也可以定義一個函數,把年齡轉換成一個數組中的第三維的索引。

      使用表驅動法的好處是,當費率改變的時候,我們用不著依次改每個條件,直接修改這個費率表就行了,即使是新加條件,也只需要把這個費率表再加一維,修改一下根據條件獲取費率的函數就行了

      還有另一個好處就是,完全可以把這個數據表保存到文件中,在程序運行的時候讀取這個文件,這樣如果條件不變只是各個條件對應的數據變化時,直接修改這個文件就行了,甚至不用修改程序。

      另一個復雜的例子是打印文件中存儲的信息

      一個文件中存儲了很多信息,這些信息可以分為幾十種,每個信息通過首部的一個信息ID分區

      如果用傳統的邏輯方法,步驟大概是:

      1. 讀取每條消息的ID
      2. 然后大量的if…else…或switch判斷消息類型
      3. 針對每種消息調用相應的處理函數

      即使是面向對象的方法,也會為每種消息定義一個類,這樣每添加一種消息都需要添加一個條件條件判斷或添加一個類。

      那么用表驅動法怎么解決呢?

      每條消息都由一些字段組成,這些字段是有限的,比如數字,字符串,布爾類型,日期等。我們可以用另外一個文件記錄每個消息對應的字段,如下所示

      “Message Description” 
      Field1 Float “Prompt” 
      Field2 Date “Created Date”

      每一種消息都通過上面的方式描述,所有消息類型被組織成一張表,這樣讀取消息的步驟就變為了:

      • 讀取消息ID
      • 找到消息ID定義的消息描述
      • 讀取描述中的每一個字段,根據字段的類型調用相應的打印方法

      這樣就只需要為每個字段類型定義一個打印方法,所有的消息打印方法都是一樣的,除非增加字段的種類,否則即使添加消息類型也不需要修改代碼

      表數據的訪問

      表數據的訪問方法我不打算按《代碼大全》中的分類方法劃分為:直接訪問、索引訪問與階梯訪問,這些只是針對特定的表的較優的訪問方法,像費率計算中的年齡條件,由于年齡是分段的,但也只需要進行一個轉換,可以說是直接訪問,根據年齡的區間可以把年齡分為不同的段,也可以說是階段訪問。(可以把各段的端點也保存到文件中增加靈活性)

      像前面的計算每月的天數的問題,直接以月份為下標就能訪問需要的數據,或者像費率計算中的年齡條件,通過一個轉換就可以取到需要的數據

      還有的情況是不方便直接訪問到數據的問的情況是,比如你有100個商品,編號為0-9999,這些編號是無規律的,你無法根據商品編號獲取表鍵,如果直接用商品編號為鍵,那就需要建立一個10000項的表,而其中只有100項有意義,如果商品相關的數據項很大會浪費很多空間,此時可以使用索引技術,建立一個100項的表存儲商品,然后建立一個10000項的索引表存儲商品編號到商品表的表鍵的映射。這樣會浪費一個索引表,但所幸是索引表的表項一般很小,問題不大。

      即使應用索引沒有節約空間而是浪費了空間,應用索引也可能會節約時間,比如查詢數據庫。

      當然,上面的例子只是為了說明情況,如果數據不是存儲在文件中而是在內存中,HashMap就行了,不過如果數據結構很復雜,計算HashCode也需要時間,當然可以優化HashCode的計算,如進行緩存等。還有稀疏矩陣等方法。

      實際應用

      表驅動法有沒有實際的應用?平時我們肯定或多或少想到了這個方法,只是就像設計模式一樣,大多數時候的思考只停留在當下的問題中,而沒有形成一個思想。當看到這個方法時我第一個想到的是Android啟動init時的init.rc。

      以下是init.rc中zygote相關配置項:

      service zygote /system/bin/app_process -Xzygote/system/bin --zygote --start-system-server
          class main
          socket zygote stream 660 root system
          onrestart write /sys/android_power/request_state wake
          onrestart write /sys/power/state on
          onrestart restart media
          onrestart restart netd

      像這段代碼中的writerestart關鍵字,我們很容易看出這是一些指令,但是系統是怎么將這些關鍵字對應到相應的指令上的?這就要看一個有意思的文件:keywords.h

      #ifndef KEYWORD
      int do_chroot(int nargs, char **args);
      int do_chdir(int nargs, char **args);
      ...
      int do_restart(int nargs, char **args);
      ...
      int do_write(int nargs, char **args);
      int do_copy(int nargs, char **args);
      ...
      int do_wait(int nargs, char **args);
      #define __MAKE_KEYWORD_ENUM__
      #define KEYWORD(symbol, flags, nargs, func) K_##symbol,
      enum {
          K_UNKNOWN,
      #endif
          KEYWORD(capability,  OPTION,  0, 0)
          KEYWORD(chdir,       COMMAND, 1, do_chdir)
          ...
          KEYWORD(restart,     COMMAND, 1, do_restart)
          ...
          KEYWORD(write,       COMMAND, 2, do_write)
          KEYWORD(copy,        COMMAND, 2, do_copy)
          ...
          KEYWORD(ioprio,      OPTION,  0, 0)
      #ifdef __MAKE_KEYWORD_ENUM__
          KEYWORD_COUNT,
      };
      #undef __MAKE_KEYWORD_ENUM__
      #undef KEYWORD
      #endif

      使用keywords.h的地方在init_parser.c

      #include "keywords.h"
      
      #define KEYWORD(symbol, flags, nargs, func) \
          [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
      
      static struct {
          const char *name;
          int (*func)(int nargs, char **args);
          unsigned char nargs;
          unsigned char flags;
      } keyword_info[KEYWORD_COUNT] = {
          [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
      #include "keywords.h"
      };
      #undef KEYWORD
      在init_parser.c中include了兩次keywords.h

      第一次時還沒有#defile KEYWORD,所以會定義do_restartdo_write等函數,并且在內部字義KEYWORD

      #define KEYWORD(symbol, flags, nargs, func) K_##symbol,
      這樣就會走這一句
      enum {
          K_UNKNOWN,
      一連串的KEYWORD定義會被這樣轉換
      KEYWORD(restart,     COMMAND, 1, do_restart) -> K_restart,
      最終的結果就是定義了一個enum:
      enum {
          K_UNKNOWN,
          ...
          K_restart,
          ...
          K_write,
          ...
          KEYWORD_COUNT,
      }
      在第一次#include "keywords.h"后,init_parser.c中DEFINE了KEYWORD,并聲明了一個結構數組
      #define KEYWORD(symbol, flags, nargs, func) \
          [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
      
      static struct {
          const char *name;
          int (*func)(int nargs, char **args);
          unsigned char nargs;
          unsigned char flags;
      } keyword_info[KEYWORD_COUNT] = {
          [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
      #include "keywords.h"
      };
      #undef KEYWORD
      由于此處定義了KEYWORD,所以再次#include "keywords.h"#ifndef KEYWORD內的語句就不會走,這一串KEYWORD宏被被這樣轉化:
      KEYWORD(restart,     COMMAND, 1, do_restart) -> [ K_restart ] = { "restart", do_restart, 2, COMMAND },
      其中COMMAND會也會被轉化,只是上面沒寫
      #define SECTION 0x01
      #define COMMAND 0x02
      #define OPTION  0x04

      這樣第二次include之后,就聲明了一個結構數組keyword_info,結構的成員分別是操作名、操作對應的處理函數、參數個數、操作類型。

      通過對keywords.h的兩次include,init進程成功定義了一個枚舉和一張表,并且以枚舉為鍵查找表可以找到相應的處理函數,這樣就不用每次獲得操作類型后查收處理函數了,直接讀表就行了,這就是前面說的,表中不只可以存數據,還可以存指令、函數指針等。雖然從init.rc的命令名找到對應的枚舉名也需要查找,但從枚舉名到處理函數的查找方便多了。

      posted @ 2014-05-29 16:58  AngelDevil  閱讀(12406)  評論(5)    收藏  舉報
      主站蜘蛛池模板: 在线观看亚洲精品国产| 国产精品亚洲А∨天堂免| 天台县| 国产精品国产精品一区精品| 久久中文字幕一区二区| 久久久婷婷成人综合激情| 国产日韩av一区二区在线| 国产女人喷潮视频免费| 116美女极品a级毛片 | 无线乱码一二三区免费看| 久久这里都是精品二| 九九热在线视频观看精品| 无遮挡粉嫩小泬久久久久久久| 午夜在线欧美蜜桃| 亚洲成人av免费一区| 日韩精品国产二区三区| 亚洲成人av高清在线| 最近2019免费中文字幕8| 艳妇乳肉豪妇荡乳在线观看| a4yy私人毛片| 免费看欧美全黄成人片| 亚洲国产美女精品久久久 | 国内精品自线在拍| 免费无码高H视频在线观看| 欧美高清精品一区二区| 国产精品熟女一区二区三区| 91网站在线看| 99久久亚洲综合精品网| 一区二区三区精品视频免费播放| 麻豆国产va免费精品高清在线| 国产在线国偷精品产拍| 亚洲夂夂婷婷色拍ww47| 国产精品va无码一区二区| 一边吃奶一边做动态图| 亚洲精品韩国一区二区| 国产欧美综合在线观看第十页| 久久精品国产亚洲av成人| 伊人久久大香线蕉av一区二区| 亚洲综合无码久久精品综合| 国产亚洲精品2021自在线| 亚洲国产一区二区三区最新|