數據即代碼,我和我的小夥伴們都驚呆了

2020-12-12 開源中國

幾個小夥伴在考慮下面這個各個語言都會遇到的問題:

問題:設計一個命令行參數解析API

一個好的命令行參數解析庫一般涉及到這幾個常見的方面:

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

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

3) 支持單字符選項、多字符選項、標誌選項、參數選項等多種選項和位置參數

4) 支持選項默認值,比如:–port選項若未指定認為5037

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

經過一番考察,小夥伴們發現了這個幾個有代表性的API設計:

1. getopt():

getopt()是libc的標準函數,很多語言中都能找到它的移植版本。

 

//Cwhile ((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 executableprogram :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}" endend$ 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庫:

//JavaScriptvar 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了,看到這個代碼之後怎麼感覺前面全是在裝呢?有個小夥伴居然激動得哭著表示:我寫代碼多年,以為再也沒有什麼代碼可以讓我感動,沒想到這段代碼如此精妙,我不由得要讚嘆了,實在是太漂亮了!

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

在絕大多數語言中數據和代碼可以說是涇渭分明,習慣C++、Java等主流語言的程式設計師很少去思考數據和代碼之間的關係。與多數語言不同的是Lisp以「數據即代碼,代碼即數據」著稱,Lisp用S表達式統一了數據和代碼的形式而獨樹一幟。Lisp奇怪的S表達式和複雜的宏系統讓許多人都感到Lisp很神秘,而多數Lisp教程要麼強調函數式編程,要麼鼓吹宏如何強大,反而掩蓋了Lisp真正本質的東西,為此我曾寫過一篇《Lisp的永恆之道》介紹Lisp思想。

設計思想和具體技術的區別在於前者往往可以在不同的環境中以不同的形式展現出來。比如,熟悉函數式編程的程式設計師在理解了純函數的優點後即使是用C語言也會更傾向於寫出無副作用的函數來,這就是函數式思想在命令式環境的應用。所以,理解Lisp思想一定要能在非Lisp環境應用,才算是融匯貫通。

如果真正理解了Lisp的本質,那所謂的「數據即代碼,代碼即數據」一點兒也不神秘,這不就是我們每天打交道的配置文件嗎!?如果你還不是很理解的話,我們通過下面幾個問題慢慢分析:

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

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

2) 配置是數據還是代碼?

很明顯,Both!說配置是數據,因為它是聲明式的描述,能方便地修改和傳輸;說配置是代碼,因為它在表達邏輯,你的程序實際上就是配置的解釋器。

3) 配置的格式是什麼?

配置的格式是任意的,可以自己定義語法,只要配以相應的解釋器就行。不過更簡單通用的做法是基於XML、JSON、或S表達式等標準結構,在此之上進一步定義schema。甚至完全不必是文件,在我們的項目中配置經常是放到用關係資料庫中的。另外,下面我們還會看到用語言的Literal數據作為配置。

4) 業務邏輯都可以放到配置中嗎?

這個問題的答案顯然是:Yes!我沒有遇到過不可以放入配置的邏輯,只是問題在於這樣做是否值得,能達到什麼效果。對於需要靈活變化,重複出現,有復用價值的東西放入作為配置是明智的選擇。這篇文章的主要目的就在於介紹把主要業務邏輯都放到配置中,再通過程序解釋執行配置的設計方法,我稱之為:元驅動編程(Meta Driven Programming)

出處 酷殼 – CoolShell.cn

相關焦點

  • 實拍日本「真豬」料理,服務員上菜時我和我的小夥伴們都驚呆了
    實拍日本「真豬」料理,服務員上菜的時候我和我的小夥伴們都驚呆了,隨著時代的發展,人們生活水平也越來越高。很多人都會對飲食很講究,各樣格式的餐飲店盛行起來,美食隨處可見。同行業的多了,競爭壓力自然就大所以很多商家都會換著法子在吸引客戶,各種各樣,五花八門的什麼都有圖片來源於網絡,如有侵權請告知刪除講到美食那就不得不提日本料理了,日本美食文化,我們都知道講究精緻
  • 快來圍觀毛坦廠中學的高考成績,我和我的小夥伴們都驚呆了
    毛坦中學,位於安徽省六安市,簡稱「毛中」,每年高考前後都是大家關注的焦點,有「亞洲最大高考工廠」之稱。每年高考時節送考人數高達萬人,成為了安徽省內的一道「高考景觀」。人數之多,所以高考成績自然也就成為了人們更加關注的焦點,以毛坦廠中學每年高考成績來看,一向都是喜報頻傳,今年應該也不會例外。7月23日0點之後全國各省份,開始陸陸續續公布各自省份的高考錄取分數線和高考成績,安徽省也在7月23日,公布了本省的高考錄取分數線和高考成績,安徽本科一批分數線:理科515,文科541;本科二批:理科435,文科499。
  • 便民|火車票還能「積分換新」我和我的小夥伴們都驚呆了!
    就由「申請受理」變為「處理完畢」在積分明細中也可以看到補登的積分上帳啦積分積累到一定數值就可以換取免費火車票嘍小編知道你們還有一些小疑問Q:所有的車票都可以得到常旅客積分嗎?A:首先,使用積分兌換的車票不能再次累計積分。注意!代用票、列車補票、到站補票、非實名制車票等暫不參與乘車積分累積。
  • 網絡流行語讓大夥伴們驚呆了
    「宅男、文藝女青年、女神……老媽,這你都不懂!」  「你的QQ籤名『冷無缺』是什麼意思?」  「就是冷漠、無理想、缺乏信仰的縮寫。」  ……  近日,有家長「吐槽」:孩子整天說「我和我的小夥伴們都驚呆了」,到底是什麼意思啊,這句話是哪裡來的?學校開學,橫幅上竟然寫著「親,歡迎來學校上課哦」,這是公然贊成學生使用這種語言嗎?
  • 拯救不規範數據表設計?這次我動用了代碼,5分鐘搞定
    進階君把這些案例提煉出來,通過清晰明了的分析思路和操作步驟,試圖給小夥伴們帶來啟發和思考,更好地解決工作任務,提高工作效率,不再做不停加班的表哥,表姐。歡迎各位小夥伴轉發、點讚、討論,更歡迎私信獲取練習素材,刻意練習才能學有收穫。這是 EXCEL進階課堂 · 案例析 的第3篇教程。
  • 看到美國孩子的暑假作業,我和小夥伴們都驚呆了!
    很多人都說中國的教育與美國的教育差異很大,那是為什麼呢? 今天讓思思君來給大家介紹下美國小學生的暑假作業,讓作為中國家長的你了解一下教育差異背後的信息。煮一些吃的37 READ THE COMICS 看漫畫38 MAKE AN ART PROJECT 完成一個藝術項目39 WRITER A POEM 寫一首詩40 PALY WITH CLAY OR PLAY DOUGH 玩泥巴或玩橡皮泥41 PLAY WITH YOUR BROTHER OR SISITER 和兄弟姐們一起玩
  • 8個Python數據清洗代碼,拿來即用
    如果你經歷過數據清洗的過程,你就會明白我的意思。而這正是撰寫這篇文章的目的——讓讀者更輕鬆地進行數據清洗工作。事實上,我在不久前意識到,在進行數據清洗時,有一些數據具有相似的模式。也正是從那時起,我開始整理並編譯了一些數據清洗代碼(見下文),我認為這些代碼也適用於其它的常見場景。
  • 我和小夥伴驚呆了
    李天一因家世和個人作風備受網民關注。李雙江們的護短,只會讓仇視增加,公眾仇富仇官就是這樣來的!」  夢鴿的「護短」,源自華西都市報,6月26號是說她「要求為兒子辯護的難度比較高」,到了第二天的京華時報上,乾脆變成了她要求律師「寫一公告,稱李天一沒有參與涉嫌強姦案」。雖然記者們所引述的都是未具名的「知情人」,但似乎也沒多少人關心說法的真假,只是被一下子撩起了快要淡忘的記憶。
  • 勵志:12位早起的IT大佬們讓小夥伴們都驚呆了
    據雅虎財經調查顯示,CEO們的一個共同特點是早起。他們早上有很多的事情,包括工作、生活等各個方面,這些通常不會輕易的改變,他們認為這就是他們的「工作方式」。許多高科技的大佬們都很少睡覺,但把事情都做得井井有條。每天叫醒他們的不是鬧鐘,而是夢想,具體來說可能是一場產品發布會、電子郵件和鍛鍊等。下面就讓我們來看看那些被夢想叫醒的成功人士。
  • 24歲小鮮肉拿出他的筆記本後……小夥伴們都驚呆了
    他的物理學筆記 / 定律和方程和大家分享一下他的兩句話:好的設計師,必先是明眼達人,設計的學習,始於磨鍊眼力和眼界的修行,所見所聞所行積累到能讓自己雙眼清明,妙劣能瞭然於心。很多事急不得,你得等它慢慢熟,大學四年有沒有努力我不清楚,唯一的願望也一直在做的就是不辱繪事,至少對得起光陰歲月。不得不說:有天賦有顏又努力,服
  • 驚呆!我孩子上的早教藝術課和你們都不一樣……
    很多家長也通過陪著孩子上課也逐步的對於藝術產生了以往沒有的興趣,今天我們很榮幸的邀請到了咻咻媽媽來和我們聊一聊讓她「驚呆」了的藝術課。① 您大概是什麼時間了解到搖籃兔早教的?當時最讓您感興趣的地方是?咻咻媽媽咻咻之前一直在上線下早教課,音樂、藝術、音樂課都上的非常歡快,就在今年年初疫情爆發後,線下課都暫停了,在我們響應國家號召閉門不出的時候,我就通過老師們了解到了搖籃兔真人直播早教,頓時感覺,真的是太牛了,能夠極速的順應時勢,把課程推薦給有需要的我們。
  • 一共撤出了12個整理盒,帶店的夥伴驚呆了
    奉節二批2015年7月31日袁良群家書 付出才有回報,要幫助更多的夥伴!前些天我們區長找我談話,問我要不要給我加那3分,讓我過4級。我立馬說的是加,問題又來了,為什麼加分,我做了什麼能得到這個加分,我說了很多很多,我工作如何努力,帶徒弟如何認真。對於這些點都不足以得到這3分,因為那些都是我身為車間主任應該做到的。無可厚非,我沒有為我的4級考試提前做準備工作,我只局限於自己的店裡,沒有把自己的能力擴大化。想明白這個道理後我更加明確我的目標,我的方向。
  • 第一次看愛5這一集的時候,我和我的小夥伴都驚呆了!
    愛情公寓13集第一次把彈幕互動劇搬上了熒幕,經常混B站的應該對彈幕文化和互動劇都不陌生,但是第一次引入到電視劇裡,確實是很新奇,這一集中,根據觀看者的互動選項可以決定公寓裡主角團的劇情走向,可以說是讓人眼前一亮了。據說拍這1集的時候,主角團們一個場景反反覆覆拍了好多遍,自己都拍蒙圈了,只要上映才知道這一集是演的什麼,導演這一波腦洞開得確實可以。
  • 「我的小夥伴們不理我了」:孩子遇到社交障礙,父母要積極面對
    最近一段時間,每天晚上,我喜歡帶著姐妹倆去小區小廣場玩耍。我尤其喜歡看凌寶跟她的小夥伴們一起玩活力板。幾個女孩還在釘釘建了一個小群,每天在群裡約:「今晚幾點下去?」或者 「我7:30下樓,你們早點出來。」
  • 簡單 敏捷 零代碼 | 外部數據管理開箱即用
    缺乏標準、缺乏整合、缺乏共享,這些管理上的缺失直接導致了數據接入慢、服務開放流程繁瑣等問題。跳出遲緩與傳統的泥潭,找到外部數據管理的捷徑——簡單敏捷零代碼。外部數據共享難:缺乏外部數據統一管理,無法快捷的將已落地數據轉化成服務,缺乏統一的數據服務集市,無法提供數據服務目錄查詢入口,自助申請、開通數據權限,在線測試、查看文檔及管理應用,便捷共享數據服務。
  • 簡單實用的數據清洗代碼
    一周前,我在領英上提問並解答了一些數據科學家和從業人員關於數據科學炙手可熱的問題。如果您一直關注我的工作,我在LinkedIn上忙於實現共享學習環境的民主化,以數據科學上為主,通過有抱負的數據科學家和其他不同專業知識和背景的數據專家的LinkedIn上發起討論。 如果您想參與有關數據科學的有趣話題的討論,請隨時LinkedIn關注我。
  • 城垣遺址的青磚,興福寺的光陰,和虞山腳下的夥伴們都是我的念想
    夏天是什麼樣子,早已忘記,人類就是一個健忘的群體,只追逐下一步的風景,常熟不是我最好的旅行計劃,卻是我非常心動的方向,包括興福寺裡的土申師父告訴我寺廟裡的秋天,和我想像以外牆外面那座城市的秋天是什麼樣子。
  • 程式設計師們,請你們不要排斥零代碼
    概括來說,以明道云為代表的零代碼平臺主要用於企業中後臺應用領域,尤其是圍繞數據管理和工作流相關的應用類別,他們一般都用於企業內部,有時候也會延伸到外部客戶和合作夥伴。這些應用都圍繞數據的增刪查改和靈活的工作流程管理而建立,用戶通過瀏覽器和行動裝置進行使用。這段概括的確已經將企業軟體行業中的很多場景都包括在內了。
  • 坑爹代碼 | Stream 玩得最 6 的代碼,看過的人都驚呆了
    Java 8 中的 Stream 是對集合(Collection)對象功能的增強,它專注於對集合對象進行各種非常便利、高效的聚合操作(aggregate operation),或者大批量數據操作 (bulk data operation)。Stream API 藉助於同樣新出現的 Lambda 表達式,極大的提高編程效率和程序可讀性。
  • 華風愛科:合作夥伴使用我的天氣數據,不僅免費,還能分成
    華風愛科CEO 馮 雪登錄華風愛科的網站,在一片藍色的地球大洲背景下,「數據訪問量」以每秒幾萬次的速度上升著。長度13位的數字顯示,目前總訪問量已超過1萬億次。雖然華風愛科這個名字還不為人熟知,但它提供的服務與每個人息息相關。三星、華為、小米、Oppo、Vivo等主流手機上,內置的「天氣預報」數據都是由華風愛科提供的。