乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      !!!數(shù)據(jù)即代碼:元驅(qū)動(dòng)編程 我和我的小伙伴們都驚呆了

       看見就非常 2013-08-15

      幾個(gè)小伙伴在考慮下面這個(gè)各個(gè)語言都會(huì)遇到的問題:

      問題:設(shè)計(jì)一個(gè)命令行參數(shù)解析API

      一個(gè)好的命令行參數(shù)解析庫一般涉及到這幾個(gè)常見的方面:

      1) 支持方便地生成幫助信息

      2) 支持子命令,比如:git包含了push, pull, commit等多種子命令

      3) 支持單字符選項(xiàng)、多字符選項(xiàng)、標(biāo)志選項(xiàng)、參數(shù)選項(xiàng)等多種選項(xiàng)和位置參數(shù)

      4) 支持選項(xiàng)默認(rèn)值,比如:–port選項(xiàng)若未指定認(rèn)為5037

      5) 支持使用模式,比如:tar命令的-c和-x是互斥選項(xiàng),屬于不同的使用模式

      經(jīng)過一番考察,小伙伴們發(fā)現(xiàn)了這個(gè)幾個(gè)有代表性的API設(shè)計(jì):

      1. getopt():

      getopt()是libc的標(biāo)準(zhǔn)函數(shù),很多語言中都能找到它的移植版本。

       

      01//C
      02while ((c = getopt(argc, argv, "ac:d:")) != -1) {
      03    int this_option_optind = optind ? optind : 1;
      04    switch (c) {
      05    case 'a':
      06        printf ("option a");
      07        aopt = 1;
      08        break;
      09    case 'c':
      10        printf ("option c with value '%s'", optarg);
      11        copt = optarg;
      12        break;
      13    case 'd':
      14        printf ("option d with value '%s'", optarg);
      15        dopt = optarg;
      16        break;
      17    case '?':
      18        break;
      19    default:
      20        printf ("?? getopt returned character code 0%o ??", c);
      21    }
      22}

      getopt()的核心是一個(gè)類似printf的格式字符串的命令行參數(shù)描述串,如上面的”ac:d:”定義了”a”, “c”,”d”3個(gè)命令行參數(shù),其中,a是一個(gè)標(biāo)志符不需要參數(shù),”c”和”d”需要跟參數(shù)。getopt()功能非常弱,只支持單個(gè)字符的標(biāo)志選項(xiàng)和參數(shù)選項(xiàng)。如果按上面的5點(diǎn)來比對(duì),基本上只能說是勉強(qiáng)支持第3點(diǎn),其他幾項(xiàng)只能靠程序自己來實(shí)現(xiàn)了,所以,想直接基于getopt()實(shí)現(xiàn)一個(gè)像git這樣復(fù)雜的命令行參數(shù)是不可能的,只有自己來做很多的解析工作。小伙伴們看過getopt()之后一致的評(píng)價(jià)是:圖樣圖森破。

      2. Google gflags

      接著,小伙伴們又發(fā)現(xiàn)了gflags這個(gè)Google出品C++命令行參數(shù)解析庫。

      01//C++
      02DEFINE_bool(memory_pool, false, "If use memory pool");
      03DEFINE_bool(daemon, true, "If started as daemon");
      04DEFINE_string(module_id, "", "Server module id");
      05DEFINE_int32(http_port, 80, "HTTP listen port");
      06DEFINE_int32(https_port, 443, "HTTPS listen port");
      07 
      08int main(int argc, char** argv) {
      09    ::google::ParseCommandLineFlags(&argc, &argv, true);
      10 
      11    printf("Server module id: %s", FLAGS_module_id.c_str());
      12 
      13    if (FLAGS_daemon) {
      14      printf("Run as daemon: %d", FLAGS_daemon);
      15    }
      16    if (FLAGS_memory_pool) {
      17      printf("Use memory pool: %d", FLAGS_daemon);
      18    }
      19 
      20    Server server;
      21 
      22    return 0;
      23}

      小伙伴們看了后不由得感嘆“真心好用啊”!的確,gflags簡單地通過幾個(gè)宏就定義了命令行選項(xiàng),基本上很好的支持了上面提到的1,3,4這幾項(xiàng),比起getopt()來強(qiáng)多了。對(duì)于類似cp這樣的小命令,gflags應(yīng)該是夠用了,但要達(dá)到git這種級(jí)別就顯得有些單薄了。

      3. Ruby Commander

      接下來小伙伴們又發(fā)現(xiàn)了Ruby Commander庫:

      01//Ruby
      02# :name is optional, otherwise uses the basename of this executable
      03program :name, 'Foo Bar'
      04program :version, '1.0.0'
      05program :description, 'Stupid command that prints foo or bar.'
      06command :bar do |c|
      07  c.syntax = 'foobar bar [options]'
      08  c.description = 'Display bar with optional prefix and suffix'
      09  c.option '--prefix STRING', String, 'Adds a prefix to bar'
      10  c.option '--suffix STRING', String, 'Adds a suffix to bar'
      11  c.action do |args, options|
      12    options.default :prefix => '(', :suffix => ')'
      13    say "#{options.prefix}bar#{options.suffix}"
      14  end
      15end
      16$ foobar bar
      17# => (bar)
      18$ foobar bar --suffix '}' --prefix '{'
      19# => {bar}

      Commander庫利用Ruby酷炫的語法定義了一種描述命令行參數(shù)的內(nèi)部DSL,看起來相當(dāng)高端大氣上檔次。除了上面的第5項(xiàng)之外,其他幾項(xiàng)都有很好的支持,可以說Commander庫的設(shè)計(jì)基本達(dá)到了git這種級(jí)別命令行參數(shù)解析的要求。只是,要搞懂Ruby這么炫的語法和這個(gè)庫的使用方法恐怕就不如getopt()和gflags容易了。有小伙伴當(dāng)場表示想要學(xué)習(xí)Ruby,但是也有小伙伴表示再看看其他庫再說。

      4. Lisp cmdline庫

      接下來,小伙伴們發(fā)現(xiàn)了Lisp方言Racket的cmdline庫。

      01//Lisp
      02(parse-command-line "compile" (current-command-line-arguments)
      03  `((once-each
      04     [("-v" "--verbose")
      05      ,(lambda (flag) (verbose-mode #t))
      06      ("Compile with verbose messages")]
      07     [("-p" "--profile")
      08      ,(lambda (flag) (profiling-on #t))
      09      ("Compile with profiling")])
      10    (once-any
      11     [("-o" "--optimize-1")
      12      ,(lambda (flag) (optimize-level 1))
      13      ("Compile with optimization level 1")]
      14     [("--optimize-2")
      15      ,(lambda (flag) (optimize-level 2))
      16      (("Compile with optimization level 2,"
      17        "which implies all optimizations of level 1"))])
      18    (multi
      19     [("-l" "--link-flags")
      20      ,(lambda (flag lf) (link-flags (cons lf (link-flags))))
      21      ("Add a flag <lf> for the linker" "lf")]))
      22   (lambda (flag-accum file) file)
      23   '("filename"))

      這是神馬浮云啊?括號(hào)套括號(hào),看起來很厲害的樣子,但又不是很明白。看到這樣的設(shè)計(jì),有的小伙伴連評(píng)價(jià)都懶得評(píng)價(jià)了,但也有的小伙伴對(duì)Lisp越發(fā)崇拜,表示Lisp就是所謂的終極語言了,沒有哪門語言能寫出這么不明覺歷的代碼來!小伙伴們正準(zhǔn)備打完收工,突然…

      5. Node.js的LineParser庫

      發(fā)現(xiàn)了Node.js的LineParser庫:

      01//JavaScript
      02var meta = {
      03    program : 'adb',
      04    name : 'Android Debug Bridge',
      05    version : '1.0.3',
      06    subcommands : [ 'connect', 'disconnect', 'install' ],
      07    options : {
      08        flags : [
      09            [ 'h', 'help', 'print program usage' ],
      10            [ 'r', 'reinstall', 'reinstall package' ],
      11            [ 'l', 'localhost', 'localhost' ]
      12        ],
      13        parameters : [
      14            [ null, 'host', 'adb server hostname or IP address', null ],
      15            [ 'p', 'port', 'adb server port', 5037 ]
      16        ]
      17    },
      18    usages : [
      19        [ 'connect', ['host', '[port]'], null, 'connect to adb server', adb_connect ],
      20        [ 'connect', [ 'l' ], null, 'connect to the local adb server', adb_connect ],
      21        [ 'disconnect', null, null, 'disconnect from adb server', adb_disconnect ],
      22        [ 'install', ['r'], ['package'], 'install package', adb_install ],
      23        [ null, ['h'], null, 'help', adb_help ],
      24    ]
      25};
      26 
      27try {
      28    var lineparser = require('lineparser');
      29    var parser = lineparser.init(meta);
      30    // adb_install will be invoked
      31    parser.parse(['install', '-r', '/pkgs/bird.apk']);
      32}
      33catch (e) {
      34    console.error(e);
      35}

      天啊?。窟@是什么?我和小伙伴們徹底驚呆了!短短十幾行代碼就獲得了上面5點(diǎn)的全面支持,重要的是小伙伴們居然一下子就看懂了,沒有任何的遮遮掩掩和故弄玄虛。本來以為Ruby和Lisp很酷,小伙伴們都想馬上去學(xué)Ruby和Lisp了,看到這個(gè)代碼之后怎么感覺前面全是在裝呢?有個(gè)小伙伴居然激動(dòng)得哭著表示:我寫代碼多年,以為再也沒有什么代碼可以讓我感動(dòng),沒想到這段代碼如此精妙,我不由得要贊嘆了,實(shí)在是太漂亮了!

      小伙伴們的故事講完了,您看懂了嗎?如果沒有看懂的話,正題開始了:

      在絕大多數(shù)語言中數(shù)據(jù)和代碼可以說是涇渭分明,習(xí)慣C++、Java等主流語言的程序員很少去思考數(shù)據(jù)和代碼之間的關(guān)系。與多數(shù)語言不同的是Lisp以“數(shù)據(jù)即代碼,代碼即數(shù)據(jù)”著稱,Lisp用S表達(dá)式統(tǒng)一了數(shù)據(jù)和代碼的形式而獨(dú)樹一幟。Lisp奇怪的S表達(dá)式和復(fù)雜的宏系統(tǒng)讓許多人都感到Lisp很神秘,而多數(shù)Lisp教程要么強(qiáng)調(diào)函數(shù)式編程,要么鼓吹宏如何強(qiáng)大,反而掩蓋了Lisp真正本質(zhì)的東西,為此我曾寫過一篇《Lisp的永恒之道》介紹Lisp思想。

      設(shè)計(jì)思想和具體技術(shù)的區(qū)別在于前者往往可以在不同的環(huán)境中以不同的形式展現(xiàn)出來。比如,熟悉函數(shù)式編程的程序員在理解了純函數(shù)的優(yōu)點(diǎn)后即使是用C語言也會(huì)更傾向于寫出無副作用的函數(shù)來,這就是函數(shù)式思想在命令式環(huán)境的應(yīng)用。所以,理解Lisp思想一定要能在非Lisp環(huán)境應(yīng)用,才算是融匯貫通。

      如果真正理解了Lisp的本質(zhì),那所謂的“數(shù)據(jù)即代碼,代碼即數(shù)據(jù)”一點(diǎn)兒也不神秘,這不就是我們每天打交道的配置文件嗎?。咳绻氵€不是很理解的話,我們通過下面幾個(gè)問題慢慢分析:

      1) 配置的本質(zhì)是什么?為什么要在程序中使用配置文件?

      不知道你是否意識(shí)到了,我們每天都在使用的各種各樣的配置本質(zhì)上是一種元數(shù)據(jù)也是一種DSL,這和Lisp基于S表達(dá)式的“數(shù)據(jù)即代碼,代碼即數(shù)據(jù)”沒有本質(zhì)區(qū)別。在C++、Java等程序中引入配置文件的目的正是用DSL彌補(bǔ)通用語言表達(dá)能力和靈活性的不足。我知道不少人喜歡從計(jì)算的角度來看到程序和語言,似乎只有圖靈完備的語言如C++、Java、Python等才叫程序設(shè)計(jì)語言,而類似CSS和HTML這樣的東西根本不能叫做程序設(shè)計(jì)語言。其實(shí),在我看來這種觀點(diǎn)過于狹隘,程序的本質(zhì)是語義的表達(dá),而語義表達(dá)不一定要是計(jì)算。

      2) 配置是數(shù)據(jù)還是代碼?

      很明顯,Both!說配置是數(shù)據(jù),因?yàn)樗锹暶魇降拿枋?,能方便地修改和傳輸;說配置是代碼,因?yàn)樗诒磉_(dá)邏輯,你的程序?qū)嶋H上就是配置的解釋器。

      3) 配置的格式是什么?

      配置的格式是任意的,可以自己定義語法,只要配以相應(yīng)的解釋器就行。不過更簡單通用的做法是基于XML、JSON、或S表達(dá)式等標(biāo)準(zhǔn)結(jié)構(gòu),在此之上進(jìn)一步定義schema。甚至完全不必是文件,在我們的項(xiàng)目中配置經(jīng)常是放到用關(guān)系數(shù)據(jù)庫中的。另外,下面我們還會(huì)看到用語言的Literal數(shù)據(jù)作為配置。

      4) 業(yè)務(wù)邏輯都可以放到配置中嗎?

      這個(gè)問題的答案顯然是:Yes!我沒有遇到過不可以放入配置的邏輯,只是問題在于這樣做是否值得,能達(dá)到什么效果。對(duì)于需要靈活變化,重復(fù)出現(xiàn),有復(fù)用價(jià)值的東西放入作為配置是明智的選擇。這篇文章的主要目的就在于介紹把主要業(yè)務(wù)邏輯都放到配置中,再通過程序解釋執(zhí)行配置的設(shè)計(jì)方法,我稱之為:元驅(qū)動(dòng)編程(Meta Driven Programming)。

      出處 酷殼 – CoolShell.cn

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多