技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 编程语言 --> 数据即代码,我和小伙伴们都惊呆了!

数据即代码,我和小伙伴们都惊呆了!

浏览:3412次  出处信息

   几个小伙伴在考虑下面这个各个语言都会遇到的问题:

   问题:设计一个命令行参数解析API

   一个好的命令行参数解析库一般涉及到这几个常见的方面:

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

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

   3) 支持单字符选项、多字符选项、标志选项、参数选项等多种选项和位置参数

   4) 支持选项默认值,比如:-port选项若未指定认为5037

   5) 支持使用模式,比如:tar命令的-c和-x是互斥选项,属于不同的使用模式

   经过一番考察,小伙伴们发现了这个几个有代表性的API设计:

   1. getopt():

   getopt()是libc的标准函数,很多语言中都能找到它的移植版本。

//C
while ((c = getopt(argc, argv, "ac:d:")) != -1) {
    int this_option_optind = optind ? optind : 1;
    switch (c) {
    case 'a':
        printf ("option a");
        aopt = 1;
        break;
    case 'c':
        printf ("option c with value '%s'", optarg);
        copt = optarg;
        break;
    case 'd':
        printf ("option d with value '%s'", optarg);
        dopt = optarg;
        break;
    case '?':
        break;
    default:
        printf ("?? getopt returned character code 0%o ??", c);
    }
}

   getopt()的核心是一个类似printf的格式字符串的命令行参数描述串,如上面的”ac:d:”定义了”a”, “c”,”d”3个命令行参数,其中,a是一个标志符不需要参数,”c”和”d”需要跟参数。getopt()功能非常弱,只支持单个字符的标志选项和参数选项。如果按上面的5点来比对,基本上只能说是勉强支持第3点,其他几项只能靠程序自己来实现了,所以,想直接基于getopt()实现一个像git这样复杂的命令行参数是不可能的,只有自己来做很多的解析工作。小伙伴们看过getopt()之后一致的评价是:图样图森破。

   2. Google gflags

   接着,小伙伴们又发现了gflags这个Google出品C++命令行参数解析库。

//C++
DEFINE_bool(memory_pool, false, "If use memory pool");
DEFINE_bool(daemon, true, "If started as daemon");
DEFINE_string(module_id, "", "Server module id");
DEFINE_int32(http_port, 80, "HTTP listen port");
DEFINE_int32(https_port, 443, "HTTPS listen port");
 
int main(int argc, char** argv) {
    ::google::ParseCommandLineFlags(&argc, &argv, true);
 
    printf("Server module id: %s", FLAGS_module_id.c_str());
 
    if (FLAGS_daemon) {
      printf("Run as daemon: %d", FLAGS_daemon);
    }
    if (FLAGS_memory_pool) {
      printf("Use memory pool: %d", FLAGS_daemon);
    }
 
    Server server;
 
    return 0;
}

   小伙伴们看了后不由得感叹“真心好用啊”!的确,gflags简单地通过几个宏就定义了命令行选项,基本上很好的支持了上面提到的1,3,4这几项,比起getopt()来强多了。对于类似cp这样的小命令,gflags应该是够用了,但要达到git这种级别就显得有些单薄了。

   3. Ruby Commander

   接下来小伙伴们又发现了Ruby Commander库:

//Ruby
# :name is optional, otherwise uses the basename of this executable
program :name, 'Foo Bar'
program :version, '1.0.0'
program :description, 'Stupid command that prints foo or bar.'
command :bar do |c|
  c.syntax = 'foobar bar [options]'
  c.description = 'Display bar with optional prefix and suffix'
  c.option '--prefix STRING', String, 'Adds a prefix to bar'
  c.option '--suffix STRING', String, 'Adds a suffix to bar'
  c.action do |args, options|
    options.default :prefix => '(', :suffix => ')'
    say "#{options.prefix}bar#{options.suffix}"
  end
end
$ foobar bar
# => (bar)
$ foobar bar --suffix '}' --prefix '{'
# => {bar}

   Commander库利用Ruby酷炫的语法定义了一种描述命令行参数的内部DSL,看起来相当高端大气上档次。除了上面的第5项之外,其他几项都有很好的支持,可以说Commander库的设计基本达到了git这种级别命令行参数解析的要求。只是,要搞懂Ruby这么炫的语法和这个库的使用方法恐怕就不如getopt()和gflags容易了。有小伙伴当场表示想要学习Ruby,但是也有小伙伴表示再看看其他库再说。

   4. Lisp cmdline库

   接下来,小伙伴们发现了Lisp方言Racket的cmdline库

//Lisp
(parse-command-line "compile" (current-command-line-arguments)
  `((once-each
     [("-v" "--verbose")
      ,(lambda (flag) (verbose-mode #t))
      ("Compile with verbose messages")]
     [("-p" "--profile")
      ,(lambda (flag) (profiling-on #t))
      ("Compile with profiling")])
    (once-any
     [("-o" "--optimize-1")
      ,(lambda (flag) (optimize-level 1))
      ("Compile with optimization level 1")]
     [("--optimize-2")
      ,(lambda (flag) (optimize-level 2))
      (("Compile with optimization level 2,"
        "which implies all optimizations of level 1"))])
    (multi
     [("-l" "--link-flags")
      ,(lambda (flag lf) (link-flags (cons lf (link-flags))))
      ("Add a flag <lf> for the linker" "lf")]))
   (lambda (flag-accum file) file)
   '("filename"))

   这是神马浮云啊?括号套括号,看起来很厉害的样子,但又不是很明白。看到这样的设计,有的小伙伴连评价都懒得评价了,但也有的小伙伴对Lisp越发崇拜,表示Lisp就是所谓的终极语言了,没有哪门语言能写出这么不明觉历的代码来!小伙伴们正准备打完收工,突然…

   5. Node.js的LineParser库

   发现了Node.js的LineParser库:

//JavaScript
var meta = {
    program : 'adb',
    name : 'Android Debug Bridge',
    version : '1.0.3',
    subcommands : [ 'connect', 'disconnect', 'install' ], 
    options : {
        flags : [
            [ 'h', 'help', 'print program usage' ],
            [ 'r', 'reinstall', 'reinstall package' ],
            [ 'l', 'localhost', 'localhost' ]
        ],
        parameters : [
            [ null, 'host', 'adb server hostname or IP address', null ],
            [ 'p', 'port', 'adb server port', 5037 ]
        ]
    },
    usages : [
        [ 'connect', ['host', '[port]'], null, 'connect to adb server', adb_connect ],
        [ 'connect', [ 'l' ], null, 'connect to the local adb server', adb_connect ],
        [ 'disconnect', null, null, 'disconnect from adb server', adb_disconnect ],
        [ 'install', ['r'], ['package'], 'install package', adb_install ],
        [ null, ['h'], null, 'help', adb_help ],
    ]
};
 
try {
    var lineparser = require('lineparser');
    var parser = lineparser.init(meta);
    // adb_install will be invoked
    parser.parse(['install', '-r', '/pkgs/bird.apk']); 
}
catch (e) {
    console.error(e);
}

   天啊!?这是什么?我和小伙伴们彻底惊呆了!短短十几行代码就获得了上面5点的全面支持,重要的是小伙伴们居然一下子就看懂了,没有任何的遮遮掩掩和故弄玄虚。本来以为Ruby和Lisp很酷,小伙伴们都想马上去学Ruby和Lisp了,看到这个代码之后怎么感觉前面全是在装呢?有个小伙伴居然激动得哭着表示:我写代码多年,以为再也没有什么代码可以让我感动,没想到这段代码如此精妙,我不由得要赞叹了,实在是太漂亮了!

   小伙伴们的故事讲完了,您看懂了吗?如果没有再看一遍标题吧!

建议继续学习:

  1. 海量数据面试题举例    (阅读:8965)
  2. 三种东西永远不要放到数据库里    (阅读:6476)
  3. vim的一个js代码整理的插件jsbeautify.vim    (阅读:4754)
  4. 如何对统计数据进行分析    (阅读:3922)
  5. 写代码这件事    (阅读:3964)
  6. 多些时间能少写些代码    (阅读:3521)
  7. 从数据中了解用户——数据在现有产品改版设计中的应用    (阅读:3387)
  8. 从数据中了解用户——数据在新产品设计中的应用    (阅读:3293)
  9. 给你的代码《约法四章》:基本功能、错误处理、智能纠错、日志收集    (阅读:3198)
  10. 如何写出无法维护的代码    (阅读:3018)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1