懶惰模式

<i>"懶惰、傲慢、缺乏耐性是程序員的三大美德"——Larry Wall(Perl's father)</i>

很久以前,《設(shè)計模式》是本“紅寶書“之類的讀物,你要是面試時談些"模式"總會有加分,同事有時也說,"哦,這里是個singleton模式, 那里是個clone模式"。今天,我們不怎么談模式,遇到問題時,總結(jié)出一些套路,有時套路有用,有時套路卻行不通,成為阻礙。

我想,程序設(shè)計里“思而不學(xué)則die, 學(xué)而不思則money",是一個普遍問題,因此,我有點打算,介紹一些更為簡單的”模式”,”簡單“但能引起"本質(zhì)”思考,希望這些”簡單“的模式能夠幫助你多思考。

模式之——懶惰

  • c語言允許這樣的語句
    my_assert = (true | (1/0)>millon);
    我們知道(1/0)根本不會發(fā)生,這被稱為短路求值,

  • 三目運算符,類似
    gain = stupid_test ? million : (1/0);

  • 嫌 if 太啰嗦,三目又不好看,有人希望寫一個漂亮的inline函數(shù),

      int my_if(test, trueValue, falseValue)
      {
          if(test) trueValue else falseValue;
      }
      //usage
      gain = my_if(stupid_test, 1000000, (1/0) )
    

    然而,他發(fā)現(xiàn)這樣根本不行!(1/0)總是被求值!

  • scala的解決方案,

    def my_if(p: boolean, true_value : =>int, false_value : => int) 
    gain = my_if(stupid_test, 1000000, (1/0) )
    //perfect
    

    解釋:
    =>int是一個表示這里是函數(shù)類型,輸入為空,返回為int,

  • c#的辦法
    int my_if(bool p, func<int> trueValue, func<int> falseValue)
    {
    if(p) trueValue() else falseValue()
    }
    //丑了點,但將就
    my_if(true, ()=>million, ()=>1/0 )

lazy模式說的是,按需求值,只有在必要時,計算才發(fā)生
你可能已經(jīng)發(fā)現(xiàn),傳入函數(shù)是實現(xiàn)lazy的關(guān)鍵

上面是些無用的例子,說些實際的例子吧:

  • log 系統(tǒng)
    在c#, java一類代碼里面, 我們常常有c語言里不存在的煩惱(因為沒有宏),比如說,
    內(nèi)循環(huán)里,做log是一件需要很小心的事,因為我們常常寫成這種形式,
    if(log_level > LOG_DEBUG) log.e( string.format("state: %d, %d, %d", getx(), gety(), getz()) )

    在未打開調(diào)試開關(guān)時,這里只造成一個 if的開銷,如果沒有<i>if</i>, <i>string.format</i> 總會發(fā)生,造成有影響的開銷,但寫起來還是麻煩的。
    解決辦法呢,almost no,一個比較丑陋,但有效的辦法是:
    void lazy_log(debug_lv, Func<string> cont)
    {
    if(log_level > LOG_DEBUG) log.w(cont());
    }
    //usage,未開調(diào)試前,format不會發(fā)生
    lazy_log(log_level, ()=>string.format("state: %d, %d, %d", getx(), gety(), getz()) )
    (真正的辦法還是需要DSL上的一些支持)

  • 單例,
    static T _ins = null;
    static T getInstance()
    {
    if(_ins == null) _ins = new T(); //第一次發(fā)生
    return _ins;
    }
    首次getInstance時,將實例初始化,這也是一個按需求值的例子,
    我們想象一個有很多module的系統(tǒng),系統(tǒng)之間存在依賴性,比如說moduleA, 需要moduleB先啟動,

有時我們顯式地去調(diào)用它.

  void system_ini()
  {
    ModuleA.init()
    ModuleB.init()
    ...
  }

這樣的話,我們需要顯式維護初始化過程,但使用單例,我們只需要getInstance,讓正確的初始化順序自動發(fā)生!

  • 衍生出的 memory 模式
    在求值時,我們總在第一次求值,之后將之存于字典(而不先將之計算存于字典),這是一種空間與時間的均衡的技術(shù),
    int hard_calc(int i)
    {
    if(_cache.find(i) ) return _cache.get(i);//
    int result;
    ////very hard word...
    _cache.add(i, result);
    return result;
    }
    如果你做過游戲物件管理,這種模式到處可見。

  • 懶惰的本質(zhì),是按需求值
    在大多數(shù)編程語言里,傳入函數(shù)的參數(shù),是一種嚴(yán)格求值,但并非是所有語言都是嚴(yán)格求值,但,還有一類語言,具有不同的求值策略,比如說 haskell,

    懶惰,但有想象力,你可以定義一個無限長自然數(shù)組,

    • from_2 = [2..] #它并不實際發(fā)生,當(dāng)你foreach遍歷取值時,真正的求值才發(fā)生。
      然后定義過濾器。
    • filter pred #它將一個流轉(zhuǎn)成另一個流,當(dāng)你foreach遍歷時,里面值按需生成
      然后我們?yōu)V去這個數(shù)組所有被第一個值整除的數(shù)
    • filte from_2 (/x -> x / 2 == 0) ,同樣,它不是對序列上的所有值立刻發(fā)生,
      它得到 [3..]
      重復(fù)最后一點,我們得到這樣的數(shù)列,
      [2..] [3..] [5..] [7..] ...
      很眼熟吧,埃拉托斯特尼篩法, haskell 里算法很直接
      from_2 = [2..]
      sieve list = h : (sieve $ filter (% h) $ t)
      where h = head list
      t = tail list
      first_20_prims = take 20 $ sieve from_2
  • 好吧,我用c#來寫一個,

    //[...2,3,4,5,6...]
      IEnumerable<int> from_n(int n)
      {
          int i = n;
          while(true)
              yield return i++;
      }
    
      IEnumerable<int> filter(IEnumerable<int> src, Func<int, bool> pred)
      {
          foreach(var i in src)
              if(pred(i))
                  yield return i;
      }
    
      IEnumerable<int> sieve(IEnumerable<int> src)
      {
          var factor = src.First();
          yield return factor;
          var rest = sieve(filter(src, x => x % factor != 0));
          foreach (var i in rest)
              yield return i;
      }
    
      static void Main(string[] args)
      {
          Program p = new Program();
          var prims = p.sieve(p.from_n(2));
          foreach(var i in prims.Take(1100))
          {
              Console.WriteLine(i);
          }
      }
    

    也許你會說,無論是hakell 或是c#實現(xiàn),都用到了語言的”特別"支持,但,如果經(jīng)過了深入思考(lazy), 你也 可以在c/c++ 做類似實現(xiàn),本來我打算寫一個,因為懶惰, 這個問題留給你 (注:這題有難度,提示,實現(xiàn)類似IEnumerator接口的iterator,你寫得多短,證明你有多(理解)lazy)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,786評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,964評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,354評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,554評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,106評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,918評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,093評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,342評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,839評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,107評論 2 375

推薦閱讀更多精彩內(nèi)容