<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢(mèng)蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    2012年5月19日


    很久沒有更新博客,沒想到更新是搬遷公告。這個(gè)博客累計(jì)的訪問量突破百萬,是我建立的時(shí)候完全沒有想過的事情。博客對(duì)我來說更多是記錄、記憶的地方,我時(shí)常因?yàn)橄氩黄鹉硞€(gè)東西,來翻自己的博客,查找舊知,發(fā)現(xiàn)新知。閱讀很多人的博客,也是我跟蹤、學(xué)習(xí)新知的主要方式。雖然微博興起,不過博客作為更系統(tǒng)性的記錄的地方,不會(huì)過時(shí)。

    非常感謝blogjava提供這么優(yōu)秀的平臺(tái)。只是我今年給自己的一個(gè)目標(biāo)是建立自己的博客,因此現(xiàn)在要搬遷,加上其實(shí)現(xiàn)在也寫的少,其實(shí)搬遷不搬遷,意義也不大了。算是一個(gè)通告,有興趣的可以訂閱我的新博客,沒興趣的請(qǐng)自行略過,謝謝大家。

    新博客地址:http://blog.fnil.net/
    RSS地址:http://blog.fnil.net/index.php/feed

    新博客的第一篇記憶是《Leiningen教程中文版》,從現(xiàn)在開始,這個(gè)博客將不再發(fā)布任何新的文章,已有的也不會(huì)刪除,部分可能會(huì)導(dǎo)到我的知識(shí)庫上去。

    最后,祝福blogjava越辦越好。

    posted @ 2012-12-10 01:24 dennis 閱讀(11896) | 評(píng)論 (6)編輯 收藏

    It's my weekend project——node-shorten: URL Shortener just like t.cn,goo.gl etc.

    Is is written in NodeJS,using express.js for MVC framework,and using MySQL for storage and Redis for caching.

    A demo online: http://fnil.me/

    The project is at https://github.com/killme2008/node-shorten

    Feel free to modify and use it.Have fun.

    posted @ 2012-11-25 20:31 dennis| 編輯 收藏

    很久沒寫博客,一是工作忙,二是沒有太多的事情可說。

    最近在公司大佬的支持下,建立了一個(gè)Clojure語言中文方面的博客和問答網(wǎng)站,歡迎任何對(duì)Clojure這門基于JVM之上的函數(shù)式語言感興趣的童鞋貢獻(xiàn)原創(chuàng)文章或者資料,申請(qǐng)帳號(hào)請(qǐng)看這里。

    博客地址:  http://blog.clojure.cn/
    問答網(wǎng)站:  http://ask.clojure.cn/

    歡迎轉(zhuǎn)發(fā)和注冊(cè)使用,謝謝。

    郵件列表仍然使用google group:https://groups.google.com/group/cn-clojure/

    posted @ 2012-09-25 12:51 dennis 閱讀(12146) | 評(píng)論 (4)編輯 收藏

    Home: https://github.com/killme2008/ring.velocity

    A Clojure library designed to render velocity template for ring in clojure.

    Usage

    Adds dependency in leiningen project.clj:

      [ring.velocity "0.1.0-SNAPSHOT"] 

    Create a directory named templates in your project directory to keep all velocity templates.

    Create a template templates/test.vm:

      hello,$name,your age is $age. 

    Use ring.velocity in your namespace:

      (use '[ring.velocity.core :only [render]]) 

    Use render function to render template with vars:

      (render "test.vm" :name "dennis" :age 29) 

    The test.vm will be interpreted equals to:

      hello,dennis,your age is 29. 

    Use ring.velocity in compojure:

      (defroutes app-routes      
    (GET "/" [] (render "test.vm" :name "dennis" :age 29))
    (route/not-found "Not Found"))

    Use ring.velocity in ring:

      (use '[ring.util.response])   
    (response (render "test.vm" :name "dennis" :age 29))

    Custom velocity properties,just put a file named ring-velocity.properties to your classpath or resource paths.The default velocity properties is in src/default/velocity.properties.

    License

    Copyright © 2012 dennis zhuang[killme2008@gmail.com]

    Distributed under the Eclipse Public License, the same as Clojure.

    Home: https://github.com/killme2008/ring.velocity

    posted @ 2012-07-18 00:07 dennis 閱讀(9507) | 評(píng)論 (0)編輯 收藏


        Clojure的一大優(yōu)點(diǎn)就是跟Java語言的完美配合,Clojure和Java之間可以相互調(diào)用,Clojure可以天然地使用Java平臺(tái)上的豐富資源。在Clojure里調(diào)用一個(gè)類的方法很簡(jiǎn)單,利用dot操作符:

    user=> (.substring "hello" 3)
    "lo"
    user=> (.substring "hello" 0 3)
    "hel"

        上面的例子是在clojure里調(diào)用String的substring方法做字符串截取。Clojure雖然是一門弱類型的語言,但是它的Lisp Reader還是能識(shí)別大多數(shù)常見的類型,比如這里hello是一個(gè)字符串就可以識(shí)別出來,3是一個(gè)整數(shù)也可以,通過這些類型信息可以找到最匹配的substring方法,在生成字節(jié)碼的時(shí)候避免使用反射,而是直接調(diào)用substring方法(INVOKEVIRTUAL指令)。

        但是當(dāng)你在函數(shù)里調(diào)用類方法的時(shí)候,情況就變了,例如,定義substr函數(shù):
    (defn substr [s begin end] (.substring s begin end))

        我們打開*warn-on-reflection*選項(xiàng),當(dāng)有反射的時(shí)候告警:

    user=> (set! *warn-on-reflection* true)
    true
    user=> (defn substr [s begin end] (.substring s begin end))
    Reflection warning, NO_SOURCE_PATH:22 - call to substring can't be resolved.
    #'user/substr
       
        問題出現(xiàn)了,由于函數(shù)substr里沒有任何關(guān)于參數(shù)s的類型信息,為了調(diào)用s的substring方法,必須使用反射來調(diào)用,clojure編譯器也警告我們調(diào)用substring沒辦法解析,只能通過反射調(diào)用。眾所周知,反射調(diào)用是個(gè)相對(duì)昂貴的操作(對(duì)比于普通的方法調(diào)用有)。這一切都是因?yàn)閏lojure本身是弱類型的語言,對(duì)參數(shù)或者返回值你不需要聲明類型而直接使用,Clojure會(huì)自動(dòng)處理類型的轉(zhuǎn)換和調(diào)用。ps.在leiningen里啟用反射警告很簡(jiǎn)單,在project.clj里設(shè)置:

    ;; Emit warnings on all reflection calls.
      :warn-on-reflection true
       
    過多的反射調(diào)用會(huì)影響效率,有沒有辦法避免這種情況呢?有的,Clojure提供了type hint機(jī)制,允許我們幫助編譯器來生成更高效的字節(jié)碼。所謂type hint就是給參數(shù)或者返回值添加一個(gè)提示:hi,clojure編譯器,這是xxx類型,我想調(diào)用它的yyy方法,請(qǐng)生成最高效的調(diào)用代碼,謝謝合作:
    user=> (defn substr [^String s begin end] (.substring s begin end))
    #'user/substr
         
        這次沒有警告,^String就是參數(shù)s的type hint,提示clojure編譯器說s的類型是字符串,那么clojure編譯器會(huì)從java.lang.String類里查找名稱為substring并且接收兩個(gè)參數(shù)的方法,并利用invokevirtual指令直接調(diào)用此方法,避免了反射調(diào)用。除了target對(duì)象(這里的s)可以添加type hint,方法參數(shù)和返回值也可以添加type hint:
    user=> (defn ^{:tag String} substr [^String s ^Integer begin ^Integer end] (.substring s begin end))
    #'user/substr
        
        返回值添加type hint是利用tag元數(shù)據(jù),提示substr的返回類型是String,其他函數(shù)在使用substr的時(shí)候可以利用這個(gè)類型信息來避免反射;而參數(shù)的type hint跟target object的type hint一樣以^開頭加上類型,例如這里begin和end都提示說是Integer類型。

        問題1,什么時(shí)候應(yīng)該為參數(shù)添加type hint呢?我的觀點(diǎn)是,在任何為target object添加type hint的地方,都應(yīng)該相應(yīng)地為參數(shù)添加type hint,除非你事先不知道參數(shù)的類型。為什么呢?因?yàn)閏lojure查找類方法的順序是這樣:

    1.從String類里查找出所有參數(shù)個(gè)數(shù)為2并且名稱為substring方法
    2.遍歷第一步里查找出來的Method,如果你有設(shè)置參數(shù)的type hint,則
    查找最匹配參數(shù)類型的Method;否則,如果第一步查找出來的Method就一個(gè),直接使用這個(gè)Method,相反就認(rèn)為沒有找到對(duì)應(yīng)的Method。
    3.如果第二步?jīng)]有找到Method,使用反射調(diào)用;否則根據(jù)該Method元信息生成調(diào)用字節(jié)碼。

       因此,如果substring方法的兩個(gè)參數(shù)版本剛好就一個(gè),方法參數(shù)有沒有type hint都沒有關(guān)系(有了錯(cuò)誤的type hint反而促使反射的發(fā)生),我們都會(huì)找到這個(gè)唯一的方法;但是如果目標(biāo)方法的有多個(gè)重載方法并且參數(shù)相同,而只是參數(shù)類型不同(Java里是允許方法的參數(shù)類型重載的,Clojure只允許函數(shù)的參數(shù)個(gè)數(shù)重載),那么如果沒有方法參數(shù)的type hint,Clojure編譯器仍然無法找到合適的調(diào)用方法,而只能通過反射。
       
       看一個(gè)例子,定義get-bytes方法調(diào)用String.getBytes:

    user=> (defn get-bytes [s charset] (.getBytes s charset))
    Reflection warning, NO_SOURCE_PATH:26 - call to getBytes can't be resolved.
    #'user/get-bytes
    user=> (defn get-bytes [^String s charset] (.getBytes s charset))
    Reflection warning, NO_SOURCE_PATH:27 - call to getBytes can't be resolved.
    #'user/get-bytes

        第一次定義,s和charset都沒有設(shè)置type hint,有反射警告;第二次,s設(shè)置了type hint,但是還是有反射警告。原因就在于String.getBytes有兩個(gè)重載方法,參數(shù)個(gè)數(shù)都是一個(gè),但是接收不同的參數(shù)類型,一個(gè)是String的charset名稱,一個(gè)Charset對(duì)象。如果我們明確地知道這里charset是字符串,那么還可以為charset添加type hint:
    user=> (defn get-bytes [^String s ^String charset] (.getBytes s charset))
    #'user/get-bytes
       
        這次才真正的沒有警告了??偨Y(jié):在設(shè)置type hint的時(shí)候,不要只考慮被調(diào)用的target object,也要考慮調(diào)用的方法參數(shù)。

        問題2:什么時(shí)候應(yīng)該添加tag元數(shù)據(jù)呢?理論上,在任何你明確知道返回類型的地方都應(yīng)該添加tag,但是這不是教條,如果一個(gè)偶爾被調(diào)用的方法是無需這樣做的。這一點(diǎn)只對(duì)寫庫的童鞋要特別注意。

        Type hint的原理在上文已經(jīng)大概描述了下,具體到clojure源碼級(jí)別,請(qǐng)參考clojure.lang.Compiler.InstanceMethodExpr類的構(gòu)造函數(shù)和emit方法。最后,附送是否使用type hint生成substr函數(shù)的字節(jié)碼之間的差異對(duì)比:
    未使用type hint 使用type hint

      // access flags 1

      public invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

       L0

        LINENUMBER 14 L0

       L1

        LINENUMBER 14 L1

        ALOAD 1

        ACONST_NULL

        ASTORE 1

        LDC "substring"

        ICONST_2

        ANEWARRAY java/lang/Object

        DUP

        ICONST_0

        ALOAD 2

        ACONST_NULL

        ASTORE 2

        AASTORE

        DUP

        ICONST_1

        ALOAD 3

        ACONST_NULL

        ASTORE 3

        AASTORE

        INVOKESTATIC clojure/lang/Reflector.invokeInstanceMethod (Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;

       L2

        LOCALVARIABLE this Ljava/lang/Object; L0 L2 0

        LOCALVARIABLE s Ljava/lang/Object; L0 L2 1

        LOCALVARIABLE begin Ljava/lang/Object; L0 L2 2

        LOCALVARIABLE end Ljava/lang/Object; L0 L2 3

        ARETURN

        MAXSTACK = 0

        MAXLOCALS = 0

    public invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

       L0

        LINENUMBER 15 L0

       L1

        LINENUMBER 15 L1

        ALOAD 1

        ACONST_NULL

        ASTORE 1

        CHECKCAST java/lang/String

        ALOAD 2

        ACONST_NULL

        ASTORE 2

        CHECKCAST java/lang/Number

        INVOKESTATIC clojure/lang/RT.intCast (Ljava/lang/Object;)I

        ALOAD 3

        ACONST_NULL

        ASTORE 3

        CHECKCAST java/lang/Number

        INVOKESTATIC clojure/lang/RT.intCast (Ljava/lang/Object;)I

        INVOKEVIRTUAL java/lang/String.substring (II)Ljava/lang/String;

       L2

        LOCALVARIABLE this Ljava/lang/Object; L0 L2 0

        LOCALVARIABLE s Ljava/lang/Object; L0 L2 1

        LOCALVARIABLE begin Ljava/lang/Object; L0 L2 2

        LOCALVARIABLE end Ljava/lang/Object; L0 L2 3

        ARETURN

        MAXSTACK = 0

        MAXLOCALS = 0


        
        對(duì)比很明顯,沒有使用type hint,調(diào)用clojure.lang.Reflector的invokeInstanceMethod方法,使用反射調(diào)用(具體見clojure.lang.Reflector.java),而使用了type hint之后,則直接使用invokevirtual指令(其他方法可能是invokestatic或者invokeinterface等指令)調(diào)用該方法,避免了反射。
          

        參考:

    posted @ 2012-07-10 20:37 dennis 閱讀(11952) | 評(píng)論 (1)編輯 收藏


        HouseMD是淘寶的聚石寫的一個(gè)非常優(yōu)秀的Java進(jìn)程運(yùn)行時(shí)診斷和調(diào)試工具,如果你接觸過btrace,那么HouseMD也許你應(yīng)該嘗試下,它比btrace更易用,不需要寫腳本,類似strace的方式attach到j(luò)vm進(jìn)程做跟蹤調(diào)試。

        基本的安裝和使用請(qǐng)看這篇文檔《UserGuide》,恕不重復(fù)。以下內(nèi)容都假設(shè)你正確安裝了housemd。

        本文主要介紹下怎么用housemd診斷跟蹤clojure進(jìn)程。Clojure的java實(shí)現(xiàn)也是跑在JVM里,當(dāng)然也可以用housemd。

        我們以一個(gè)簡(jiǎn)單的例子開始,假設(shè)我們有如下clojure代碼:
    (loop [x 1]
      (Thread/sleep 1000)
      (prn x)
      (recur (inc x)))

        這段很簡(jiǎn)單,只是間隔一秒不斷地打印遞增的數(shù)字x。我們準(zhǔn)備用housemd跟蹤這個(gè)程序的運(yùn)行,首先運(yùn)行這個(gè)程序,你可以用lein,也可以直接java命令運(yùn)行:
    java -cp clojure.jar clojure.main test.clj

        運(yùn)行時(shí)不斷地在控制臺(tái)打印數(shù)字,通過jps或者ps查詢到該進(jìn)程的id,假設(shè)為pid,使用housemd連接到該進(jìn)程:
    housemd <pid>
        順利進(jìn)入housemd的交互控制臺(tái),通過help命令可以查詢支持的命令:

    housemd> help

    quit      terminate the process.
    help      display this infomation.
    trace     display or output infomation of method invocaton.
    loaded    display loaded classes information.

        要用housemd調(diào)試clojure,你需要對(duì)clojure的實(shí)現(xiàn)有一點(diǎn)點(diǎn)了解,有興趣可以看過去的一篇blog《clojure hacking guide》,簡(jiǎn)單來說,clojure的編譯器會(huì)將clojure代碼編譯成java類并運(yùn)行。對(duì)于JVM來說,clojure生成的類,跟java編譯器生成類沒有什么不同。
        具體到上面的clojure代碼,會(huì)生成一個(gè)名為user$eval1的類,user是默認(rèn)的namespace,而eval1是clojure編譯器自動(dòng)生成的一個(gè)標(biāo)示類名,通過loaded命令查詢類的加載情況:
    housemd> loaded user$eval1 -h
    user$eval1 -> null
        - clojure.lang.DynamicClassLoader@1d25d06e
            - clojure.lang.DynamicClassLoader@1d96f4b5
                - sun.misc.Launcher$AppClassLoader@a6eb38a
                    - sun.misc.Launcher$ExtClassLoader@69cd2e5f

        通過-h選項(xiàng)打印了加載user$eval1的類加載器的層次關(guān)系,因?yàn)閡ser$eval1是動(dòng)態(tài)生成的(clojure啟動(dòng)過程中),因此它不在任何一個(gè)class或者jar文件中。除了查詢user namespace的類之外,你還可以查詢clojure.core,clojure.lang,clojure.java等任何被加載進(jìn)來的類,例如查詢clojure.core.prn的類,在clojure里這是一個(gè)函數(shù),在jvm看來這只是一個(gè)類:
    housemd> loaded -h core$prn
    clojure.core$prn -> /Volumes/HDD/Users/apple/clojure/clojure.jar
        - sun.misc.Launcher$AppClassLoader@a6eb38a
            - sun.misc.Launcher$ExtClassLoader@69cd2e5f
       注意,不需要完整的namespace——clojure.core,直接core$prn即可。其他也是類似。小技巧:如果你實(shí)在不知道clojure編譯器生成的類名,你可以利用jvm自帶的jmap命令來查詢。

       接下來,我們嘗試用trace命令跟蹤方法的運(yùn)行,例如例子中的clojure代碼用到了loop和recur兩個(gè)sepcial form,我們跟蹤下loop:
    housemd> trace -t 5 core$loop
    INFO : probe class clojure.core$loop
    core$loop.doInvoke(Object, Object, Object, Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null
    core$loop.getRequiredArity()                          sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null

    core$loop.doInvoke(Object, Object, Object, Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null
    core$loop.getRequiredArity()                          sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null

    core$loop.doInvoke(Object, Object, Object, Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null
    core$loop.getRequiredArity()                          sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null

    core$loop.doInvoke(Object, Object, Object, Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null
    core$loop.getRequiredArity()                          sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null

    core$loop.doInvoke(Object, Object, Object, Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null
    core$loop.getRequiredArity()                          sun.misc.Launcher$AppClassLoader@a6eb38a            0            -ms    null

    INFO : Ended by timeout
    INFO : reset class clojure.core$loop

        在5秒內(nèi),clojure.core$loop類有兩個(gè)方法各被調(diào)用了5次,doInvoke是實(shí)際的調(diào)用,而getRequiredArity用來查詢loop所需要的參數(shù)個(gè)數(shù)。trace還可以跟蹤到具體的方法,例如我們跟蹤prn函數(shù)的調(diào)用情況:
    housemd> trace -t 5 core$prn.doInvoke
    INFO : probe class clojure.core$prn
    core$prn.doInvoke(Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            1            1ms    clojure.core$prn@3e4ac866

    core$prn.doInvoke(Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            2           <1ms    clojure.core$prn@3e4ac866

    core$prn.doInvoke(Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            3           <1ms    clojure.core$prn@3e4ac866

    core$prn.doInvoke(Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            4           <1ms    clojure.core$prn@3e4ac866

    core$prn.doInvoke(Object)    sun.misc.Launcher$AppClassLoader@a6eb38a            5           <1ms    clojure.core$prn@3e4ac866

    INFO : Ended by timeout
    INFO : reset class clojure.core$prn
      
       trace打印了方法的調(diào)用次數(shù)(5秒內(nèi))和每次調(diào)用的時(shí)間(毫秒級(jí)別),以及調(diào)用的target object。小技巧:沒有可變參數(shù)的函數(shù)生成類最終調(diào)用的是invoke方法(參數(shù)個(gè)數(shù)可能重載),有可變參數(shù)的函數(shù)調(diào)用的是doInvoke方法。

       trace命令還支持打印調(diào)用堆棧到文件,例如:
    trace -t 5 -d -s  core$prn.doInvoke

       利用-s和-d命令會(huì)將詳細(xì)的調(diào)用信息輸出到臨時(shí)目錄,臨時(shí)目錄的路徑可以通過trace help命令查詢到,在我的機(jī)器上是/tmp/trace/<pid>@host目錄下。調(diào)用堆棧的輸出類似:
    example$square.invoke(Long) call by thread [main]
        example$eval9.invoke(test.clj:11)
        clojure.lang.Compiler.eval(Compiler.java:6465)
        clojure.lang.Compiler.load(Compiler.java:6902)
        clojure.lang.Compiler.loadFile(Compiler.java:6863)
        clojure.main$load_script.invoke(main.clj:282)
        clojure.main$script_opt.invoke(main.clj:342)
        clojure.main$main.doInvoke(main.clj:426)
        clojure.lang.RestFn.invoke(RestFn.java:421)
        clojure.lang.Var.invoke(Var.java:405)
        clojure.lang.AFn.applyToHelper(AFn.java:163)
        clojure.lang.Var.applyTo(Var.java:518)
        clojure.main.main(main.java:37)

       上面這個(gè)簡(jiǎn)單的例子展示了使用housemd跟蹤診斷clojure進(jìn)程的方法。

       自定義ns和函數(shù)的調(diào)試與此類似,假設(shè)我們有下面的clojure代碼:
    (ns example)
    (defn square [x]
      (* x x))

    (loop [x 1]
      (Thread/sleep 1000)
      (square x)
      (recur (inc x)))
     
       ns為example,自定義函數(shù)square并定期循環(huán)調(diào)用。使用housemd診斷這段代碼:
    loaded -h example$square     #查詢square的加載情況
    trace -t 10 -d -s example$square.invoke  #跟蹤10秒內(nèi)square的調(diào)用情況

    posted @ 2012-06-15 02:52 dennis 閱讀(12163) | 評(píng)論 (2)編輯 收藏

    我們?cè)诰S護(hù)的淘寶開源消息中間件的metaqgithub分支,今天發(fā)布了1.4.2版本,主要做了如下改進(jìn):

    1.支持發(fā)送和訂閱分離,可以細(xì)粒度地控制Broker或者某個(gè)Topic是否接收消息和接受訂閱。服務(wù)端添加新選項(xiàng)acceptPublish和acceptSubscribe。

    2.更友好地關(guān)閉Broker,梳理關(guān)閉流程并通過JMX調(diào)用方法關(guān)閉替代原來簡(jiǎn)單的kill。

    3.更新python客戶端到0.2版本,可以通過pip安裝:  pip install metaq

    4.發(fā)布ruby語言客戶端meta-ruby 0.1版本。

    5.其他小改進(jìn):升級(jí)gecko到1.1.1版本,升級(jí)quartz到2.1.4版本,添加集成測(cè)試工程和內(nèi)部重構(gòu)等。

    6.新文檔《使用log4j擴(kuò)展發(fā)送消息》

    簡(jiǎn)介:https://github.com/killme2008/Metamorphosis/wiki/介紹
    下載:https://github.com/killme2008/Metamorphosis/downloads

    文檔:https://github.com/killme2008/Metamorphosis/wiki

    posted @ 2012-06-04 10:03 dennis 閱讀(11256) | 評(píng)論 (1)編輯 收藏


        你有個(gè)任務(wù),需要用到某個(gè)開源項(xiàng)目;或者老大交代你一個(gè)事情,讓你去了解某個(gè)東西。怎么下手呢?如何開始呢?我的習(xí)慣是這樣:

    1.首先,查找和閱讀該項(xiàng)目的博客和資料,通過google你能找到某個(gè)項(xiàng)目大體介紹的博客,快速閱讀一下就能對(duì)項(xiàng)目的目的、功能、基本使用有個(gè)大概的了解。

    2.閱讀項(xiàng)目的文檔,重點(diǎn)關(guān)注類似Getting started、Example之類的文檔,從中學(xué)習(xí)如何下載、安裝、甚至基本使用該項(xiàng)目所需要的知識(shí)。

    3.如果該項(xiàng)目有提供現(xiàn)成的example工程,首先嘗試按照開始文檔的介紹運(yùn)行example,如果運(yùn)行順利,那么恭喜你順利開了個(gè)好頭;如果遇到問題,首先嘗試在項(xiàng)目的FAQ等文檔里查找答案,再次,可以將問題(例如異常信息)當(dāng)成關(guān)鍵詞去搜索,查找相關(guān)的解決辦法,你遇到了,別人一般也會(huì)遇到,熱心的朋友會(huì)記錄下解決的過程;最后,可以將問題提交到項(xiàng)目的郵件列表,請(qǐng)大家?guī)湍憧纯础?strong>在沒有成功運(yùn)行example之前,不要嘗試修改example。

    4.運(yùn)行了第一個(gè)example之后,嘗試根據(jù)你的理解和需要修改example,測(cè)試高級(jí)功能等。

    5.在了解基本使用后,需要開始深入的了解該項(xiàng)目。例如項(xiàng)目的配置管理、高級(jí)功能以及最佳實(shí)踐。通常一個(gè)運(yùn)作良好的項(xiàng)目會(huì)提供一份從淺到深的用戶指南,你并不需要從頭到尾閱讀這份指南,根據(jù)時(shí)間和興趣,特別是你自己任務(wù)的需要,重點(diǎn)閱讀部分章節(jié)并做筆記(推薦evernote)。

    6.如果時(shí)間允許,嘗試從源碼構(gòu)建該項(xiàng)目。通常開源項(xiàng)目都會(huì)提供一份構(gòu)建指南,指導(dǎo)你如何搭建一個(gè)用于開發(fā)、調(diào)試和構(gòu)建的環(huán)境。嘗試構(gòu)建一個(gè)版本。

    7.如果時(shí)間允許并且有興趣,可以嘗試閱讀源碼:
    (1)閱讀源碼之前,查看該項(xiàng)目是否提供架構(gòu)和設(shè)計(jì)文檔,閱讀這些文檔可以了解該項(xiàng)目的大體設(shè)計(jì)和結(jié)構(gòu),讀源碼的時(shí)候不會(huì)無從下手。
    (2)閱讀源碼之前,一定要能構(gòu)建并運(yùn)行該項(xiàng)目,有個(gè)直觀感受。
    (3)閱讀源碼的第一步是抓主干,嘗試?yán)砬逡淮握_\(yùn)行的代碼調(diào)用路徑,這可以通過debug來觀察運(yùn)行時(shí)的變量和行為。修改源碼加入日志和打印可以幫助你更好的理解源碼。
    (4)適當(dāng)畫圖來幫助你理解源碼,在理清主干后,可以將整個(gè)流程畫成一張流程圖或者標(biāo)準(zhǔn)的UML圖,幫助記憶和下一步的閱讀。
    (5)挑選感興趣的“枝干”代碼來閱讀,比如你對(duì)網(wǎng)絡(luò)通訊感興趣,就閱讀網(wǎng)絡(luò)層的代碼,深入到實(shí)現(xiàn)細(xì)節(jié),如它用了什么庫,采用了什么設(shè)計(jì)模式,為什么這樣做等。如果可以,debug細(xì)節(jié)代碼。
    (6)閱讀源碼的時(shí)候,重視單元測(cè)試,嘗試去運(yùn)行單元測(cè)試,基本上一個(gè)好的單元測(cè)試會(huì)將該代碼的功能和邊界描述清楚。
    (7)在熟悉源碼后,發(fā)現(xiàn)有可以改進(jìn)的地方,有精力、有意愿可以向該項(xiàng)目的開發(fā)者提出改進(jìn)的意見或者issue,甚至幫他修復(fù)和實(shí)現(xiàn),參與該項(xiàng)目的發(fā)展。

    8.通常在閱讀文檔和源碼之后,你能對(duì)該項(xiàng)目有比較深入的了解了,但是該項(xiàng)目所在領(lǐng)域,你可能還想搜索相關(guān)的項(xiàng)目和資料,看看有沒有其他的更好的項(xiàng)目或者解決方案。在廣度和深度之間權(quán)衡。

        以上是我個(gè)人的一些習(xí)慣,我自己也并沒有完全按照這個(gè)來,但是按照這個(gè)順序,基本上能讓你比較高效地學(xué)習(xí)和使用某個(gè)開源項(xiàng)目。

    posted @ 2012-05-22 23:12 dennis 閱讀(25587) | 評(píng)論 (9)編輯 收藏


        很久沒更新博客了,在北京工作,忙碌并且充實(shí)。目前來說,Clojure最好的開發(fā)編輯器應(yīng)該是Emacs + Slime的組合,利用swank-clojure這個(gè)項(xiàng)目,加上clojure-mode,可以完美地運(yùn)行slime。編譯、運(yùn)行、跳轉(zhuǎn)、文檔和引用查看甚至debug都可以搞定。具體配置恕不重復(fù),看swank-clojure的文檔即可自己安裝起來,或者這篇中文博客windows上配置

        分享幾個(gè)Tip,也期待大家分享你們的使用心得。

        首先是自動(dòng)在打開clj后綴文件的時(shí)候啟動(dòng)執(zhí)行clojure-jack-in與slime連接,可以在emacs配置里加上個(gè)callback:

    (eval-after-load "clojure-mode"
      '(progn
         (require 'slime)
         (require 'clojure-mode)
         (unless (slime-connected-p)
           (save-excursion (clojure-jack-in)))))
        這樣在打開clj為后綴的文件的時(shí)候,將自動(dòng)啟動(dòng)clojure-mode執(zhí)行clojure-jack-in函數(shù)并且連接slime。

        將clj后綴的文件自動(dòng)關(guān)聯(lián)到clojure-mode:
    (setq auto-mode-alist (cons '("\\.clj$" . clojure-mode) auto-mode-alist))
        通常來說如果你是利用marmalade安裝的,會(huì)自動(dòng)關(guān)聯(lián)的。

        另外,啟動(dòng)自動(dòng)匹配括號(hào)、字符串引號(hào)等的paredit模式一定要啟動(dòng):
    (defun paredit-mode-enable () (paredit-mode 1))
    (add-hook 'clojure-mode-hook 'paredit-mode-enable)
    (add-hook 'clojure-test-mode-hook 'paredit-mode-enable)

       在使用clojure-mode或者clojure-test-mode的時(shí)候自動(dòng)啟用paredit模式,括號(hào)再也不是問題。括號(hào)匹配提示一般是開啟的,如果沒有,強(qiáng)制開啟:

    ;;    顯示括號(hào)匹配
    (show-paren-mode t)
    (setq show-paren-style 'parentheses)

        slime更多配置,啟用IO重定向(多線程IO輸出都定向到SLIME repl)以及設(shè)置通訊字符編碼等:

    (eval-after-load "slime"
      '(progn
         (slime-setup '(slime-repl slime-fuzzy))
         ;;(setq slime-truncate-lines t)
         (setq  swank:*globally-redirect-io*  t)
         ;; (setq slime-complete-symbol-function ' slime-fuzzy-complete-symbol)
         (setq slime-net-coding-system 'utf-8-unix)))

        細(xì)心的朋友可能注意到我注釋了slime-fuzzy-complete的配置,這是一個(gè)支持更好的自動(dòng)補(bǔ)全功能的SLIME插件(可以用縮寫來自動(dòng)補(bǔ)全),可惜在我機(jī)器上沒有嘗試配置成功,有興趣你可以嘗試下。

        在REPL里支持語法高亮,一定要配置上:

    (add-hook 'slime-repl-mode-hook
              (defun clojure-mode-slime-font-lock ()
                (require 'clojure-mode)
                (let (font-lock-mode)
                  (clojure-mode-font-lock-setup))))

        單獨(dú)在clojure-mode(在其他mode里這些快捷鍵不會(huì)起作用)里配置快捷鍵可以這樣:
    (eval-after-load "clojure-mode"
      '(progn
         (require 'slime)
         (require 'clojure-mode)
         (define-key clojure-mode-map (kbd "M-/")  (quote slime-complete-symbol))
         (define-key clojure-mode-map (kbd "C-c s")  (quote slime-selector)))

       例如我這里將M-/作為自動(dòng)補(bǔ)全的快捷鍵,因?yàn)閙eta鍵在我的Mac機(jī)器上設(shè)置為command鍵,因此自動(dòng)補(bǔ)全的操作習(xí)慣就跟Eclipse類似。而slime-selector是一個(gè)非常有用的函數(shù),用來跳轉(zhuǎn)到slime的一系列buffer,因此我綁定了C-c s快捷鍵。

        額外一提,在Mac osx下,將command作為meta鍵:
    ;;; I prefer cmd key for meta
    (setq mac-option-key-is-meta nil
          mac-command-key-is-meta t
          mac-command-modifier 'meta
          mac-option-modifier 'none)

        最后,期待大家不吝分享你的心得。
        

    posted @ 2012-05-19 00:57 dennis 閱讀(15314) | 評(píng)論 (11)編輯 收藏

    主站蜘蛛池模板: 亚洲日韩在线中文字幕综合| 中文字幕视频免费在线观看| 亚洲国产一成久久精品国产成人综合 | 在线观看91精品国产不卡免费| 国产精品无码亚洲精品2021| 亚洲线精品一区二区三区影音先锋| 2021国内精品久久久久精免费| 亚洲AV无码专区在线电影成人| 国产亚洲欧洲Aⅴ综合一区 | 老汉精品免费AV在线播放| 亚洲中文字幕一二三四区| 亚洲女同成人AⅤ人片在线观看 | 成年女人免费视频播放77777 | 大学生一级毛片免费看| 国产亚洲精品美女2020久久| 亚洲人成在线影院| 国产jizzjizz视频全部免费| 鲁大师在线影院免费观看| 免费看美女午夜大片| 亚洲婷婷综合色高清在线| 亚洲综合亚洲综合网成人| 国产成人无码免费看视频软件| 久久久久免费视频| 亚洲JLZZJLZZ少妇| 亚洲黄网站wwwwww| 中文字幕在线亚洲精品| 日本最新免费不卡二区在线| 97国产在线公开免费观看| xxxxx做受大片在线观看免费 | 免费一级不卡毛片| 欧洲美女大片免费播放器视频| 亚洲一区二区三区精品视频| 国产AV无码专区亚洲AV男同| 国产三级免费电影| 成年女人视频网站免费m| 少妇人妻偷人精品免费视频| 国产精品免费看久久久香蕉| 国产精品亚洲片在线花蝴蝶 | 香蕉视频在线观看免费| 亚洲色在线无码国产精品不卡 | 日韩毛片免费一二三|