日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

面向组合子程序设计方法 之 新约

發布時間:2025/3/21 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 面向组合子程序设计方法 之 新约 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
每個小孩剛開始走路的時候都是跌跌撞撞的。?
我們不自量力,妄圖踩著上帝的步伐前進。結果就是這么幾個簡單的象白開水似的類。失望嗎?是不是造物試圖模仿造物主本身就是一種可笑的狂妄呢??

別急,讓我們失聲痛哭之前先看看我們這幾步走的是不是一錢不值。?
[list]?
1。logger可以把信息打印到log文件中。 ?

容易,直接創建一個WriterLogger就好了。?

2。不同的重要程度的信息也許可以打印到不同的文件中?象websphere,有error.log, info.log等。?
如果這樣,那么什么重要程度的信息進入error.log,什么進入warning.log,什么進入info.log也需要決定。
?

不同的文件嗎?好辦啊。就是不同的PrintWriter對象了。?
Java代碼??
  • Logger?err_log?=?writer(err_writer);;??
  • Logger?warning_log?=?writer(warning_writer);;??
  • Logger?info_log?=?writer(info_writer);;??


  • 各個文件記錄不同重要程度的信息是么??
    Java代碼??
  • err_log?=?filter(ERROR,?err_log,?nop(););;??
  • warning_log?=?filter(WARNING,?warning_log,?nop(););;??
  • info_log?=?filter(INFO,?info_log,?nop(););;??

  • 最終把三個不同的logger串聯起來就是了:?
    Java代碼??
  • Logger?logger?=?sequence(err_log,?warning_log,?info_log);;??



  • 3。也許可以象ant一樣把所有的信息都打印到一個文件中。 ?

    這就更簡單了,就一個是WriterLogger。?


    4。每條logging信息是否要同時打印當前的系統時間?也是一個需要抉擇的問題。 ?

    拿不定主意是么?沒關系,想好了再告訴我。?
    反正,如果你需要系統時間,我只需要?
    Java代碼??
  • logger?=?timestamp(logger);;??





  • 5。不僅僅是log文件,我們還希望能夠在標準錯誤輸出上直接看見錯誤,普通的信息可以打印到log文件中,對錯誤信息,我們希望log文件和標準輸出上都有。 ?

    可以創建針對標準輸出的Logger,然后和打印log 文件的logger串聯起來。?
    Java代碼??
  • Logger?std_logger?=?writer(new?PrintWriter(System.err););;??
  • std_logger?=?ignore(ERROR,?std_logger);;??
  • logger?=?sequence(std_logger,?logger);;??





  • 6。標準輸出上的東西只要通知我們出錯了就行,大概不需要詳細的stack trace,所以exception stack trace可以打印到文件中,而屏幕上有個簡短的exception的message就夠了。 ?

    這里需要對std_logger稍微改寫一下:?
    Java代碼??
  • PrintWriter?out?=?new?PrintWriter(System.err);;??
  • std_logger?=?new?ErrorMessageLogger(out,?writer(out););;??
  • std_logger?=?ignore(ERROR,?std_logger,?nop(););;??

  • 用ErrorMessageLogger來改寫對異常的log邏輯。?



    7。warning似乎也應該輸出到屏幕上。 ?

    好啊。就是把ignore函數里面的ERROR換成WARNING就好了?
    Java代碼??
  • std_logger?=?ignore(WARNING,?std_logger,?nop(););;??



  • 8。不管文件里面是否要打印當前系統時間,屏幕上應該可以選擇不要打印時間。 ?

    對std_logger不掉用timestamp就是了。?

    9。客戶應該可以通過命令行來決定log文件的名字。
    ?

    這條和logger組合子其實沒什么關系。?

    10。客戶可以通過命令行來決定log的細節程度,比如,我們只關心info一樣級別的信息,至于debug, verbose的信息,對不起,不要打印。 ?

    生成那個最終使用的Logger對象的時候,再ignore一下就行了:?

    Java代碼??
  • logger?=?ignore(some_level,?logger,?nop(););;??



  • 11。neptune生成的是一些Command對象,這些對象運行的時候如果出現exception,這些exception會帶有execution trace,這個execution trace可以告訴我們每個調用棧上的Command對象在原始的neptune文件中的位置(行號)。?
    這種exception叫做NeptuneException,它有一個printExecutionTrace(PrintWriter)的方法來打印execution trace。?
    所以,對應NeptuneException,我們就不僅僅是printStackTrace()了,而是要在printStackTrace()之前調用printExecutionTrace()。
    ?

    NeptuneExceptionLogger就是給這個準備的呀。?

    12。neptune使用的是jaskell語言,如果jaskell腳本運行失敗,一個EvaluationException會被拋出,這個類有一個printEvaluationTrace(PrintWriter)的方法來打印evaluation trace,這個trace用來告訴我們每個jaskell的表達式在腳本文件中的位置。?
    所以,對應EvaluationException,我們要在printStackTrace()之前,調用printEvaluationTrace()。
    ?

    JaskellExceptionLogger?


    13。execution trace和evaluation trace應該被打印到屏幕上和log文件兩個地方。
    ?

    這就是說,上面兩個Logger應該被應用到std_logger和logger兩個對象中。?


    14。因為printExecutionTrace()和printEvaluationTrace()本身已經打印了這個異常的getMessage(),所以,對這兩種異常,我們就不再象對待其它種類的異常那樣在屏幕上打印getMessage()了,以免重復。?


    就是說,一旦一個exception被發現是NeptuneException,那么ErrorMessageLogger就要被跳過了。?
    Java代碼??
  • final?Logger?err_logger?=?new?ErrorMessageLogger(writer);;??
  • final?Logger?jaskell_logger?=?new?JaskellExceptionLogger(writer,?err_logger);;??
  • final?Logger?neptune_logger?=?new?NeptuneExceptionLogger(writer,?jaskell_logger);;??
  • return?neptune_logger;??

  • 這個neptune_logger先判斷異常是不是NeptuneException,如果是,直接處理,否則,傳遞給jaskell_logger。jaskell_logger繼續判斷,如果不是它感興趣的,再傳遞給ErrorMessageLogger來做最后的缺省處理。?


    15。也許還有一些暫時沒有想到的需求, 比如不是寫入log文件,而是畫個圖之類的變態要求
    。?

    放馬過來吧。看我們的組合子能不能對付。?
    [/list:u]?

    很驚訝地發現,就這么幾個小兒科似的積木,就似乎全部解決了曾讓我們煩惱的這些需求??

    為了給大家一個完整的印象,下面是我實際項目中使用這些組合子應對上面這些需求的代碼:?
    Java代碼??
  • public?class?StreamLogger?{??
  • ??private?final?OutputStream?out;??
  • ????
  • ??/**?
  • ???*?To?create?a?StreamLogger?object.?
  • ???*?@param?out?the?OutputStream?object?that?the?log?message?should?go?to.?
  • ???*/??
  • ??public?StreamLogger(OutputStream?out);?{??
  • ????this.out?=?out;??
  • ??}??
  • ????
  • ??/**?
  • ???*?To?get?the?OutputStream?that?the?log?messages?should?go?to.?
  • ???*/??
  • ??public?OutputStream?getStream();?{??
  • ????return?out;??
  • ??}??
  • ??private?static?Logger?getBaseLogger(PrintWriter?writer);{??
  • ????final?Logger?nop?=?new?NopLogger();;??
  • ????final?Logger?base?=?Loggers.logger(writer);;??
  • ????final?Logger?neptune_logger?=?new?NeptuneExceptionLogger(writer,?nop);;??
  • ????final?Logger?jaskell_logger?=?new?JaskellExceptionLogger(writer,?nop);;??
  • ????return?Loggers.sequence(??
  • ????????new?Logger[]{neptune_logger,?jaskell_logger,?base}??
  • ????);;??
  • ??}??
  • ??private?static?Logger?getEchoLogger(PrintWriter?writer);{??
  • ????return?new?ErrorMessageLogger(writer);;??
  • ??}??
  • ??private?static?Logger?getErrorLogger(PrintWriter?writer);{??
  • ????final?Logger?err_logger?=?new?ErrorMessageLogger(writer);;??
  • ????final?Logger?jaskell_logger?=?new?JaskellExceptionLogger(writer,?err_logger);;??
  • ????final?Logger?neptune_logger?=?new?NeptuneExceptionLogger(writer,?jaskell_logger);;??
  • ????return?neptune_logger;??
  • ??}??
  • ??/**?
  • ???*?Get?the?Logger?instance.?
  • ???*?@param?min_level?the?minimal?critical?level?for?a?log?message?to?show?up?in?the?log.?
  • ???*?@return?the?Logger?instance.?
  • ???*/??
  • ??public?Logger?getDefaultLogger(int?min_level);{??
  • ????final?PrintWriter?writer?=?new?PrintWriter(out,?true);;??
  • ????final?PrintWriter?err?=?new?PrintWriter(System.err,?true);;??
  • ????final?PrintWriter?warn?=?new?PrintWriter(System.out,?true);;??
  • ????final?Logger?all?=?Loggers.sequence(new?Logger[]{??
  • ????????Loggers.ignore(getErrorLogger(err);,?Logger.ERROR);,??
  • ????????Loggers.filter(getEchoLogger(warn);,?Logger.WARNING);,??
  • ????????getBaseLogger(writer);??
  • ??????}??
  • ????);;??
  • ????return?Loggers.ignore(all,?min_level);;??
  • ??}??
  • }??


  • 為了偷懶,我沒有用配置文件,就是把這些策略硬編碼進java了。好在上面的代碼非常declarative,改起來也很容易。?

    沒習慣讀代碼的朋友。這里奉勸還是讀一讀吧。很多時候,代碼才是說明問題的最好手段。我相信,只有讀了代碼,你才能真正嘗到CO的味道。?


    有朋友問,你這個東西和decorator pattern有什么區別呀?乍看上去,還真是長得差不多呢。不都是往現有的某個對象上面附加一些功能嗎??
    也許是把。我不知道象SequenceLogger這種接受一個數組的,是否也叫做對數組的decorator;也不知道IgnoreLogger接受了兩個Logger對象,這個decorator究竟是修飾誰的呢??

    其實,叫什么名字無所謂。我這里要講的,是一種從基本粒子推演組合的思路。形式上它也許碰巧象decorator, 象ioc。但是正如workinghard所說(這句話深得我心),思路的切入點不同。?

    如果你仔細看上面的代碼,也許你會有所感覺:對Logger的千奇百怪的組合本身已經有點象一個程序代碼了。?
    如果用偽碼表示:?

    Java代碼??
  • all_logger?=?ignore?messages?below?ERROR?for?getErrorLogger(err);;??
  • ????????filter?messages?except?WARNING?for?getEchoLogger(warn);;??
  • ????????baseBaseLogger(writer);;??
  • ignore?messages?below?lvl?for?all_logger;??

  • 當組合子越來越多,需求越來越復雜,這個組合就會越來越象個程序。?

    這里,實際上,(至少我希望如此),我們的思維已經從打印什么什么東西上升為在Logger這個級別的組裝了。?

    這也就是所謂higher order logic的意思。?


    所謂combinator-oriented,在這里,就體現為系統性地在高階邏輯的層面上考慮問題,而不是如decorator那樣的零敲碎打的幾個功能模塊。?
    大量的需求邏輯被以聲明式的方式在高階邏輯中實現,而基本的組合子只負責實現原字操作。?


    當然,缺點也是明顯的,對此我不諱言:?

    [list]高階邏輯不容易調試,當我們使用一個組合了十幾層的復雜的Logger對象的時候(真正用了co這種情況不少見),一旦出現bug,跟蹤的時候我們就會發現象是陷入了一個迷宮,從一個組合子跟蹤進入另一個組合子,繞來繞去。?

    另外,異常的stack trace也無法反映組合層次關系,造成錯誤定位麻煩。[/list:u]?


    這也許不是co本身的問題,而是因為java這種oo語言對co沒有提供語言上的支持。但是無論如何,這對在java中大規模使用co造成了障礙。?

    也許你還無法理解。平時我們在java中用那么幾個decorator,本身非常簡單,所以debug, trace都不是問題。可是,一旦oriented起來,情況就不同了。街上有兩輛車和成千上萬輛車,對交通造成的壓力截然不同。?


    還有朋友,對co如何把握有疑問。難道co就是瞎貓碰死耗子么??

    其實,無論co還是oo,對設計者都是有一定要求的。?
    oo要求你了解需求,并且經驗豐富,也要有一點點運氣。?
    co也要求經驗,這個經驗是設計組合子系統的經驗。什么樣的組合子是好的?怎么才算是足夠簡單?什么組合規則是合理的?等等,這些,也有規律可循,就像oo的各種模式一樣。同時,也可以refactor。畢竟,怎么簡單地想問題比怎么分解復雜問題可能還是要容易掌握一點。?

    不過,co對經驗的要求稍微小一點,但是對數學和邏輯的基本功要求則多了一點。有了一些數學和邏輯方面的基本功,那么設計組合子就會輕松的多。?

    co也要有一點點運氣。所以遇到你怎么想也想不明白的情況,就別死抗啦,也許這個問題就抽象不出組合子來,或者以我們普通人的智慧抽象不出來。?



    co是銀彈嗎?當然不是,至少以我的功力沒有這種自信。?
    遇到復雜的問題我也是先分解需求,面向接口的。只有問題的規模被控制在了一定的范圍,我才會試圖用co來解決問題。靠著對co的一些經驗和感覺,一旦發現了可以組合子化的概念,成果會非常顯著。?


    而且,co和oo關注的層面也不同。co是針對一個單獨的概念(這點倒有點象ao),一點一點演繹,構成一個可以任意復雜的系統,一個好的co也會大大減少需要引入的概念數。而oo是處理大量互相或者有聯系,或者沒有聯系的概念,研究怎么樣把一個看上去復雜的系統的復雜度控制住。所以兩者并不是互相排斥的。自頂向下,自底向上,也許還是兩手一起抓更好些。?

    這段時間應用co做了幾個軟件后,感覺co最擅長的領域是:?
    問題域有比較少的概念,概念簡明清晰(比如logger, predicate, factory,command),但是對概念的實現要求非常靈活的場合。?
    這種時候,用oo就有無處下嘴之感。畢竟概念已經分解了,職責也清楚,就是怎么提供一個可以靈活適應變化的體系,而對這個,oo并沒有提供一個方法論。基本上就是用po的方法吭哧吭哧硬做。而co此時就可以大展用武之地。彌補這個空隙。?



    看過圣經的,也許有感覺,舊約里面的上帝嚴厲,經典,就像是一個純粹的fp語言,每個程序員都被迫按照函數式,組合子的方式思考問題,你感覺困難?那是你不夠虔誠。你們人不合我的心意,我淹死你們!?


    而新約里面,明顯添加了很多人情味兒。上帝通過自己的兒子和人們和解了。既然淹死一波,再來一波還是這樣,那么是不是說大家應該各讓一步呢??
    co和oo,既然各自都不能宣稱自己就是銀彈,那么為什么不能拉起手來呢?我們不是神,不可能真正按照神的方式用基本粒子組合演化世界,所以就別象清教徒一樣苦苦追求不可能的目標了。但是,上帝的組合之道畢竟相當巧妙,在有些時候荊棘里面出現火焰的時候,我們又何必固執地拒絕造物主的好意呢?禮貌地說一聲:“謝了!老大”。不是皆大歡喜??


    這一節我們繼續講解了這個logging的例子。實際上,logging是一個非常簡單的組合子,下面如果大家對這個例子有疑問,我會盡力解答。?

    然后,我們會進軍下一個例子。


    from:http://ajoo.iteye.com/blog/23307

    總結

    以上是生活随笔為你收集整理的面向组合子程序设计方法 之 新约的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。