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

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

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

    莊周夢蝶

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

        今天同事遇到的問題,用JRuby調用一個java方法,該方法使用了jdk1.5的可變參數。我一開始以為只要簡單地將可變參數表示為數組即可,例如下面的兩個java類:
    public class Echo{
        
    public void echo(String name){
           System.out.println(name);
        }
    }
    public class Test{
        
    public void hello(String name,Echoargs){
            System.out.println(
    "hello,"+name);
            
    for(Echo e:args){
                e.echo(name);
            }
        }
    }
       我想在jruby中調用Test的hello方法,該方法有個可變參數args。所謂可變參數經過編譯后其實也就是數組,這個可以通過觀察字節碼知道,那么如果用數組來調用可以不?
    require 'java'
    require 
    'test.jar'
    include_class 
    'Test'
    include_class 
    'Echo'
    t.hello(
    "dennis")  #報錯,參數不匹配
    t.hello("dennis",[])  #報錯,類型不匹配
       很遺憾,這樣調用是錯誤的,原因如上面的注釋。具體到類型不匹配,本質的原因是JRuby中的數組與java中對數組的字節碼表示是不一致的,JRuby中的數組是用org.jruby.RubyArray類來表示,而hello方法需要的數組卻是是[LEcho。解決的辦法就是將JRuby的數組轉成java需要的類型,通過to_java方法,因而下面的調用才是正確的,盡管顯的麻煩:
    require 'java'
    require 
    'test.jar'
    include_class 
    'Test'
    include_class 
    'Echo'
    t
    =Test.new
    t.hello(
    "dennis",[].to_java("Echo"))
    e1
    =Echo.new
    t.hello(
    "dennis",[e1].to_java("Echo"))
    e2
    =Echo.new
    t.hello(
    "dennis",[e1,e2].to_java("Echo"))


    posted @ 2008-06-14 22:39 dennis 閱讀(2184) | 評論 (1)編輯 收藏

         頭發乃人之元,很多人看人,第一眼看的就是你的頭發,當然看MM的部位可能不一樣。我的頭發從來都是像野草一樣,從來沒放心思在上面,都是理一次頭發大半年不管他,長的跟雜草似的難受,然后回家找小區內的師傅理發——或者說剃頭,而且發型從來都是板寸頭,短短的,免的大半年不回家還得在外面理發。因而,我的理發時間跟我的回家時間一樣。

    小時候,那時候住在老家,沒有專門的理發店,也可能是離我家的小村莊很遠。有個走村串戶的剃頭匠,大概幾個月能在村里見他一次,帶著一身行頭,誰家有人要剃頭,就請他過去。記的那時是沒用電的理發工具,一把類似夾子的東西,老師傅手工在各式各樣的腦袋上推來推去,然后搞些肥皂水就著自家的井水沖洗。后來很久沒再見到這位老師傅,而隔壁村開了家理發店,大人們就帶著我們走路或者騎著自行車去隔壁村剃頭了,記的那位師傅還是我們大隊的干部,隔壁是豬肉店,至于剃頭的細節卻是記不清了。再后來隔壁村又開了一家理發店,是個小青年開的,印象特別深的一次是我奶奶帶我過去理發,小青年說如果我幫他打一桶井水,錢可以減半。我幫他打了桶井水,然后真給我減半了,就著自己打的井水沖洗我的不規則的大腦袋。后來,跟父母搬到了縣城,去外地讀書,就再也沒在老家理發過了,好幾年之后路過小青年開的那家店,裝修了,藍色的玻璃后面,小青年變成了師傅,指導著自己的徒弟在理發,我想,大概他也記不得當年那個給他打水的小屁孩了。

    進城了,我理發就固定在小區的周師傅這了。上大學,基本是五一、十一和春節回家的時候剃一次,哪怕再長,還是不愿意在福州理發,分不清是習慣還是莫名其妙的堅持了。其實挺不好意思的,每次都留那么長的頭發,周師傅也還是固定收我5塊錢,不過今年漲價了,7塊。下午剃頭的時候,周師傅的女兒找他要100來塊錢,旁邊的人問周師傅她拿去做啥呢?周師傅說去染發,語氣有點黯然。父親理的老式發型,女兒是不會喜歡的。我的頭發就這樣隨著我離家的遠近輪回著,現在在廣州,回家還是容易的,以后去哪還是不知道的事情,希望還能半年回家一次理發。

    posted @ 2008-06-13 21:54 dennis 閱讀(588) | 評論 (4)編輯 收藏

    Hadoop分布式文件系統:架構和設計要點
    原文:http://hadoop.apache.org/core/docs/current/hdfs_design.html
    一、前提和設計目標
    1、硬件錯誤是常態,而非異常情況,HDFS可能是有成百上千的server組成,任何一個組件都有可能一直失效,因此錯誤檢測和快速、自動的恢復是HDFS的核心架構目標。
    2、跑在HDFS上的應用與一般的應用不同,它們主要是以流式讀為主,做批量處理;比之關注數據訪問的低延遲問題,更關鍵的在于數據訪問的高吞吐量。
    3、HDFS以支持大數據集合為目標,一個存儲在上面的典型文件大小一般都在千兆至T字節,一個單一HDFS實例應該能支撐數以千萬計的文件。
    4、 HDFS應用對文件要求的是write-one-read-many訪問模型。一個文件經過創建、寫,關閉之后就不需要改變。這一假設簡化了數據一致性問 題,使高吞吐量的數據訪問成為可能。典型的如MapReduce框架,或者一個web crawler應用都很適合這個模型。
    5、移動計算的代價比之移動數據的代價低。一個應用請求的計算,離它操作的數據越近就越高效,這在數據達到海量級別的時候更是如此。將計算移動到數據附近,比之將數據移動到應用所在顯然更好,HDFS提供給應用這樣的接口。
    6、在異構的軟硬件平臺間的可移植性。

    二、NamenodeDatanode
        HDFS采用master/slave架構。一個HDFS集群是有一個Namenode和一定數目的Datanode組成。Namenode是一個中心服 務器,負責管理文件系統的namespace和客戶端對文件的訪問。Datanode在集群中一般是一個節點一個,負責管理節點上它們附帶的存儲。在內 部,一個文件其實分成一個或多個block,這些block存儲在Datanode集合里。Namenode執行文件系統的namespace操作,例如 打開、關閉、重命名文件和目錄,同時決定block到具體Datanode節點的映射。DatanodeNamenode的指揮下進行block的創 建、刪除和復制。NamenodeDatanode都是設計成可以跑在普通的廉價的運行linux的機器上。HDFS采用java語言開發,因此可以部 署在很大范圍的機器上。一個典型的部署場景是一臺機器跑一個單獨的Namenode節點,集群中的其他機器各跑一個Datanode實例。這個架構并不排 除一臺機器上跑多個Datanode,不過這比較少見。

    單一節點的Namenode大大簡化了系統的架構。Namenode負責保管和管理所有的HDFS元數據,因而用戶數據就不需要通過Namenode(也就是說文件數據的讀寫是直接在Datanode上)。

    三、文件系統的namespace
       HDFS支持傳統的層次型文件組織,與大多數其他文件系統類似,用戶可以創建目錄,并在其間創建、刪除、移動和重命名文件。HDFS不支持user quotas和訪問權限,也不支持鏈接(link),不過當前的架構并不排除實現這些特性。Namenode維護文件系統的namespace,任何對文 件系統namespace和文件屬性的修改都將被Namenode記錄下來。應用可以設置HDFS保存的文件的副本數目,文件副本的數目稱為文件的 replication因子,這個信息也是由Namenode保存。

    四、數據復制
        HDFS被設計成在一個大集群中可以跨機器地可靠地存儲海量的文件。它將每個文件存儲成block序列,除了最后一個block,所有的block都是同 樣的大小。文件的所有block為了容錯都會被復制。每個文件的block大小和replication因子都是可配置的。Replication因子可 以在文件創建的時候配置,以后也可以改變。HDFS中的文件是write-one,并且嚴格要求在任何時候只有一個writerNamenode全權管 理block的復制,它周期性地從集群中的每個Datanode接收心跳包和一個Blockreport。心跳包的接收表示該Datanode節點正常工 作,而Blockreport包括了該Datanode上所有的block組成的列表。


    1、副本的存放,副本的存放是HDFS可靠性和性能的關鍵。HDFS采用一種稱為rack-aware的策略來改進數據的可靠性、有效性和網絡帶寬的利 用。這個策略實現的短期目標是驗證在生產環境下的表現,觀察它的行為,構建測試和研究的基礎,以便實現更先進的策略。龐大的HDFS實例一般運行在多個機 架的計算機形成的集群上,不同機架間的兩臺機器的通訊需要通過交換機,顯然通常情況下,同一個機架內的兩個節點間的帶寬會比不同機架間的兩臺機器的帶寬 大。
        通過一個稱為Rack Awareness的過程,Namenode決定了每個Datanode所屬的rack id。一個簡單但沒有優化的策略就是將副本存放在單獨的機架上。這樣可以防止整個機架(非副本存放)失效的情況,并且允許讀數據的時候可以從多個機架讀 取。這個簡單策略設置可以將副本分布在集群中,有利于組件失敗情況下的負載均衡。但是,這個簡單策略加大了寫的代價,因為一個寫操作需要傳輸block到 多個機架。
        在大多數情況下,replication因子是3HDFS的存放策略是將一個副本存放在本地機架上的節點,一個副本放在同一機架上的另一個節點,最后一 個副本放在不同機架上的一個節點。機架的錯誤遠遠比節點的錯誤少,這個策略不會影響到數據的可靠性和有效性。三分之一的副本在一個節點上,三分之二在一個 機架上,其他保存在剩下的機架中,這一策略改進了寫的性能。

    2、副本的選擇,為了降低整體的帶寬消耗和讀延時,HDFS會盡量讓reader讀最近的副本。如果在reader的同一個機架上有一個副本,那么就讀該副本。如果一個HDFS集群跨越多個數據中心,那么reader也將首先嘗試讀本地數據中心的副本。

    3、SafeMode
        Namenode啟動后會進入一個稱為SafeMode的特殊狀態,處在這個狀態的Namenode是不會進行數據塊的復制的。Namenode從所有的 Datanode接收心跳包和Blockreport。Blockreport包括了某個Datanode所有的數據塊列表。每個block都有指定的最 小數目的副本。當Namenode檢測確認某個Datanode的數據塊副本的最小數目,那么該Datanode就會被認為是安全的;如果一定百分比(這 個參數可配置)的數據塊檢測確認是安全的,那么Namenode將退出SafeMode狀態,接下來它會確定還有哪些數據塊的副本沒有達到指定數目,并將 這些block復制到其他Datanode。

    五、文件系統元數據的持久化
        Namenode存儲HDFS的元數據。對于任何對文件元數據產生修改的操作,Namenode都使用一個稱為Editlog的事務日志記錄下來。例如, 在HDFS中創建一個文件,Namenode就會在Editlog中插入一條記錄來表示;同樣,修改文件的replication因子也將往 Editlog插入一條記錄。Namenode在本地OS的文件系統中存儲這個Editlog。整個文件系統的namespace,包括block到文件 的映射、文件的屬性,都存儲在稱為FsImage的文件中,這個文件也是放在Namenode所在系統的文件系統上。
        Namenode在內存中保存著整個文件系統namespace和文件Blockmap的映像。這個關鍵的元數據設計得很緊湊,因而一個帶有4G內存的 Namenode足夠支撐海量的文件和目錄。當Namenode啟動時,它從硬盤中讀取EditlogFsImage,將所有Editlog中的事務作 用(apply)在內存中的FsImage ,并將這個新版本的FsImage從內存中flush到硬盤上,然后再truncate這個舊的Editlog,因為這個舊的Editlog的事務都已經 作用在FsImage上了。這個過程稱為checkpoint。在當前實現中,checkpoint只發生在Namenode啟動時,在不久的將來我們將 實現支持周期性的checkpoint。
        Datanode并不知道關于文件的任何東西,除了將文件中的數據保存在本地的文件系統上。它把每個HDFS數據塊存儲在本地文件系統上隔離的文件中。 Datanode并不在同一個目錄創建所有的文件,相反,它用啟發式地方法來確定每個目錄的最佳文件數目,并且在適當的時候創建子目錄。在同一個目錄創建 所有的文件不是最優的選擇,因為本地文件系統可能無法高效地在單一目錄中支持大量的文件。當一個Datanode啟動時,它掃描本地文件系統,對這些本地 文件產生相應的一個所有HDFS數據塊的列表,然后發送報告到Namenode,這個報告就是Blockreport。

    六、通訊協議
        所有的HDFS通訊協議都是構建在TCP/IP協議上。客戶端通過一個可配置的端口連接到Namenode,通過ClientProtocolNamenode交互。而Datanode是使用DatanodeProtocolNamenode交互。從ClientProtocolDatanodeprotocol抽象出一個遠程調用(RPC),在設計上,Namenode不會主動發起RPC,而是是響應來自客戶端和 Datanode RPC請求。

    七、健壯性
        HDFS的主要目標就是實現在失敗情況下的數據存儲可靠性。常見的三種失?。?/span>Namenode failures, Datanode failures和網絡分割(network partitions)。
    1、硬盤數據錯誤、心跳檢測和重新復制
        每個Datanode節點都向Namenode周期性地發送心跳包。網絡切割可能導致一部分DatanodeNamenode失去聯系。 Namenode通過心跳包的缺失檢測到這一情況,并將這些Datanode標記為dead,不會將新的IO請求發給它們。寄存在dead Datanode上的任何數據將不再有效。Datanode的死亡可能引起一些block的副本數目低于指定值,Namenode不斷地跟蹤需要復制的 block,在任何需要的情況下啟動復制。在下列情況可能需要重新復制:某個Datanode節點失效,某個副本遭到損壞,Datanode上的硬盤錯 誤,或者文件的replication因子增大。

    2、集群均衡
       HDFS支持數據的均衡計劃,如果某個Datanode節點上的空閑空間低于特定的臨界點,那么就會啟動一個計劃自動地將數據從一個Datanode搬移 到空閑的Datanode。當對某個文件的請求突然增加,那么也可能啟動一個計劃創建該文件新的副本,并分布到集群中以滿足應用的要求。這些均衡計劃目前 還沒有實現。

    3、數據完整性
      從某個Datanode獲取的數據塊有可能是損壞的,這個損壞可能是由于Datanode的存儲設備錯誤、網絡錯誤或者軟件bug造成的。HDFS客戶端 軟件實現了HDFS文件內容的校驗和。當某個客戶端創建一個新的HDFS文件,會計算這個文件每個block的校驗和,并作為一個單獨的隱藏文件保存這些 校驗和在同一個HDFS namespace下。當客戶端檢索文件內容,它會確認從Datanode獲取的數據跟相應的校驗和文件中的校驗和是否匹配,如果不匹配,客戶端可以選擇 從其他Datanode獲取該block的副本。

    4、元數據磁盤錯誤
        FsImageEditlogHDFS的核心數據結構。這些文件如果損壞了,整個HDFS實例都將失效。因而,Namenode可以配置成支持維護多 個FsImageEditlog的拷貝。任何對FsImage或者Editlog的修改,都將同步到它們的副本上。這個同步操作可能會降低 Namenode每秒能支持處理的namespace事務。這個代價是可以接受的,因為HDFS是數據密集的,而非元數據密集。當Namenode重啟的 時候,它總是選取最近的一致的FsImageEditlog使用。
       NamenodeHDFS是單點存在,如果Namenode所在的機器錯誤,手工的干預是必須的。目前,在另一臺機器上重啟因故障而停止服務的Namenode這個功能還沒實現。

    5、快照
       快照支持某個時間的數據拷貝,當HDFS數據損壞的時候,可以恢復到過去一個已知正確的時間點。HDFS目前還不支持快照功能。

    八、數據組織
    1、數據塊
        兼容HDFS的應用都是處理大數據集合的。這些應用都是寫數據一次,讀卻是一次到多次,并且讀的速度要滿足流式讀。HDFS支持文件的write- once-read-many語義。一個典型的block大小是64MB,因而,文件總是按照64M切分成chunk,每個chunk存儲于不同的 Datanode
    2、步驟
        某個客戶端創建文件的請求其實并沒有立即發給Namenode,事實上,HDFS客戶端會將文件數據緩存到本地的一個臨時文件。應用的寫被透明地重定向到 這個臨時文件。當這個臨時文件累積的數據超過一個block的大?。J64M),客戶端才會聯系Namenode。Namenode將文件名插入文件系 統的層次結構中,并且分配一個數據塊給它,然后返回Datanode的標識符和目標數據塊給客戶端。客戶端將本地臨時文件flush到指定的 Datanode上。當文件關閉時,在臨時文件中剩余的沒有flush的數據也會傳輸到指定的Datanode,然后客戶端告訴Namenode文件已經 關閉。此時Namenode才將文件創建操作提交到持久存儲。如果Namenode在文件關閉前掛了,該文件將丟失。
       上述方法是對通過對HDFS上運行的目標應用認真考慮的結果。如果不采用客戶端緩存,由于網絡速度和網絡堵塞會對吞估量造成比較大的影響。

    3、流水線復制
        當某個客戶端向HDFS文件寫數據的時候,一開始是寫入本地臨時文件,假設該文件的replication因子設置為3,那么客戶端會從Namenode 獲取一張Datanode列表來存放副本。然后客戶端開始向第一個Datanode傳輸數據,第一個Datanode一小部分一小部分(4kb)地接收數 據,將每個部分寫入本地倉庫,并且同時傳輸該部分到第二個Datanode節點。第二個Datanode也是這樣,邊收邊傳,一小部分一小部分地收,存儲 在本地倉庫,同時傳給第三個Datanode,第三個Datanode就僅僅是接收并存儲了。這就是流水線式的復制。

    九、可訪問性
        HDFS給應用提供了多種訪問方式,可以通過DFSShell通過命令行與HDFS數據進行交互,可以通過java API調用,也可以通過C語言的封裝API訪問,并且提供了瀏覽器訪問的方式。正在開發通過WebDav協議訪問的方式。具體使用參考文檔。
    十、空間的回收
    1、文件的刪除和恢復
        用戶或者應用刪除某個文件,這個文件并沒有立刻從HDFS中刪除。相反,HDFS將這個文件重命名,并轉移到/trash目錄。當文件還在/trash目 錄時,該文件可以被迅速地恢復。文件在/trash中保存的時間是可配置的,當超過這個時間,Namenode就會將該文件從namespace中刪除。 文件的刪除,也將釋放關聯該文件的數據塊。注意到,在文件被用戶刪除和HDFS空閑空間的增加之間會有一個等待時間延遲。
        當被刪除的文件還保留在/trash目錄中的時候,如果用戶想恢復這個文件,可以檢索瀏覽/trash目錄并檢索該文件。/trash目錄僅僅保存被刪除 文件的最近一次拷貝。/trash目錄與其他文件目錄沒有什么不同,除了一點:HDFS在該目錄上應用了一個特殊的策略來自動刪除文件,目前的默認策略是 刪除保留超過6小時的文件,這個策略以后會定義成可配置的接口。

    2、Replication因子的減小
        當某個文件的replication因子減小,Namenode會選擇要刪除的過剩的副本。下次心跳檢測就將該信息傳遞給Datanode, Datanode就會移除相應的block并釋放空間,同樣,在調用setReplication方法和集群中的空閑空間增加之間會有一個時間延遲。

    參考資料:
    HDFS Java API: http://hadoop.apache.org/core/docs/current/api/
    HDFS source code: http://hadoop.apache.org/core/version_control.html
       







    posted @ 2008-06-05 14:29 dennis 閱讀(53374) | 評論 (25)編輯 收藏

        當將用scheme寫的scheme求值器跑起來的時候,你不覺的興奮是不可能的,真的太酷了,太magic了。
    習題4.2,如果將application?判斷放在define?判斷之前,那么求值(define x 3)將把define當作一般的procedure應用于參數x和3,可是define是特殊的語法形式,而非一般過程,導致出錯。
    習題4.4,我的解答,eval增加兩個判斷:
     ((and? exp)
       (eval
    -and (and-exps exp) env))
     ((or
    ? exp)
       (eval
    -or (or-exps exp) env))
    實現謂詞和各自的過程:
    (define (and? exp) 
      (tagged
    -list? exp 'and))
    (define (and-exps exp)
      (cdr exp))
    (define (eval
    -and exps env)
      (cond ((
    null? exps)
              
    'true)
            (else
              (let ((result (eval (car exps) env)))
                (
    if (not result)
                    result
                    (eval
    -and (cdr exps) env))))))
    (define (or
    ? exp)
      (tagged
    -list? exp 'or))
    (define (or-exps exp) (cdr exp))
    (define (eval
    -or exps env)
      (cond ((
    null? exps)
             
    'false)
            (else
             (let ((result (eval (car exps) env)))
               (
    if result
                   result
                   (eval
    -or (cdr exps) env))))))

    如果用宏實現就簡單了:
    (define-syntax and
      (syntax
    -rules ()
          ((_) #t)
          ((_ e) e)
          ((_ e1 e2 e3 )
            (
    if e1 (and e2 e3 ) #f))))
    (define
    -syntax or
       (syntax
    -rules ()
                 ((_) #f)
                 ((_ e) e)
                 ((_ e1 e2 e3 )
                  (let ((t e1))
                      (
    if t t (or e2 e3 ))))))

    習題4.5,cond的擴展形式,也不難,在else子句之外增加個判斷,是否帶有=>符號,修改expand-clauses過程:
    (define (cond-extended-clauses? clause)
      (and (> (length clause) 2) (eq? (cadr clause) '=>)))
    (define (extended-cond-test clause)
      (car clause))
    (define (extended-cond-recipient clause)
      (caddr clause)
    (define (expand
    -clauses clauses)
      (
    if (null? clauses)
          
    'false
          (let ((first (car clauses))
                (rest (cdr clauses)))
            (cond ((cond
    -else-clauses? first)
                    (
    if (null? rest)
                        (sequence
    ->exp (cond-actions first))
                        (error 
    "else clause is not LAST" clauses)))
                  ((cond
    -extended-clauses? first)  ;判斷是否擴展形式
                   (make
    -if
                       (extended
    -cond-test first)
                        (list
                          (extended
    -cond-recipient first)
                          (extended
    -cond-test first))
                          (expand
    -clauses rest)))
                  (
    else
                   (make
    -if (cond-predicate first)
                            (sequence
    ->exp (cond-actions first))
                            (expand
    -clauses rest)))))))

    習題4.6,let如果用宏定義,類似這樣:
    (define-syntax let
      (syntax
    -rules ()
        ((_ ((x v) ) e1 e2 )
         ((
    lambda (x ) e1 e2 ) v ))))
    求值器擴展,實現let->combination過程:
    (define (let? exp)
      (tagged
    -list? exp 'let))
    (define (let->combination exp)
      (let ((vars (map car (cadr exp)))
            (vals (map cadr (cadr exp)))
            (body (caddr exp)))
      (cons (make
    -lambda vars (list body)) vals)))
    我們做的僅僅是syntax transform,將let轉成對應的lambda形式。

    習題4.7,這里的關鍵在于let*->netsted-lets要遞歸調用,從let*的宏定義可以看出:
    (define-syntax let*
         (syntax
    -rules()
           ((_ ((var1 val1)) e1 )
            (let ((var1 val1)) e1 ))
           ((_ ((var1 val1) (var2 val2) ) e1 )
            (let ((var1 val1))
              (let
    * ((var2 val2) )
                e1 )))))
    那么,let*->nested-lets可以定義為:
    (define (let*? exp)
      (tagged
    -list? exp 'let*))
    (define (let*->nested-lets exp)
         (let ((pairs (cadr exp))
               (body (caddr exp)))
           (
    if (null? (cdr pairs))
               (list 
    'let pairs body)
               (list 'let (list (car pairs)) (let*->nested-lets (list 'let* (cdr pairs) body))))))
    測試一下:
    (let* ((x 1) (y (+ x 3))) (+ x y)) =》5

    習題4.8,命名let,修改let->combination過程,判斷cadr是pair還是symbol,如果是前者,那就是一般的let,如果是symbol就是命名let語句,那么需要定義一個名為(cadr exp)的過程放在body里,注意,我們是在做語法轉換,因此,這個定義也應該描述成符號,定義一個make-define過程來生成define語句:
    (define (make-define var parameters body)
      (list 
    'define (cons var parameters) body))
    然后修改let->combination過程,如上所述:
    (define (let->combination exp)
      (
    if (symbol? (cadr exp))
          (let ((var (cadr exp))
                (vars (map car (caddr exp)))
                (vals (map cadr (caddr exp)))
                (pairs (caddr exp))
                (body (cadddr exp)))
            (cons (make
    -lambda vars (list (make-define var vars body) body)) vals))
          (let ((vars (map car (cadr exp)))
                (vals (map cadr (cadr exp)))
                (body (caddr exp)))
                  (cons (make
    -lambda vars (list body)) vals))))


    習題4.1.4,原生的map過程接受的procedure,是以scheme內在數據結構表示的procedure,而在我們的求值器中,procedure的內在數據結構取決于我們,與原生的結構不同,導致原生的map無法接受自制求值器的procedure,如果map也以求值器中的結構定義,那么就沒有這個問題。因此,本質上的困難就在于兩個求值器對procedure的數據結構表示的不同。

    習題4.1.5,著名的圖靈停機問題,先是假設存在halts?過程可以正確地判斷任何過程p和對象a是否p對a終止,定義了try過程:
    (define (try p)
       (if (halts? p p)
           (run-forever)
            'halted))
    當執行(try try),如果這個過程可終止,那么(halts? try try)應該返回false,也就是try過程對try不會終止,這與一開始的假設矛盾;如果這個過程將無窮運行下去,那么(halts? try try)應該返回true,也就是try對try終止,這也與(try try)將無窮運行的假設矛盾。因此,可以推論出,我們不可能寫出一個過程halts?,使它能正確地判斷任何過程p和對象a是否p對a終止。

    posted @ 2008-06-01 15:51 dennis 閱讀(1034) | 評論 (0)編輯 收藏

    第一個程序:
    import java.util.ArrayList;
    import java.util.List;

    public class TailRecursionTest {
        
    public static void main(String[] args) {
            TailRecursionTest t 
    = new TailRecursionTest();
            
    for (int i = 0; i < 10000; i++)
                t.a(
    0);
        }

        
    public void a(int j) {
            j
    ++;
            List list 
    = new ArrayList<Integer>(100000);
            
    // 對list進行處理
        }
    }
        沒啥特殊的,僅僅是為了測試,我們將a方法調用10000次,a方法創建一個有100000個元素的list的局部變量。
    第二個程序:
    import java.util.ArrayList;
    import java.util.List;

    public class TailRecursionTest2 {
        
    public static void main(String[] args) {
            TailRecursionTest2 t 
    = new TailRecursionTest2();
            t.a(
    0);
        }

        
    public void a(int j) {
            System.out.println(j);
            j
    ++;
            
    if (j == 10000)
                
    return;
            List list 
    = new ArrayList<Integer>(100000);
            
    // 對list進行處理
            a(j);
        }
    }

        也沒啥特殊的,就是將循環換成了遞歸,a方法做的事情沒變。兩個都跑一下,程序1順利結束,程序2出問題了,啥問題?如下:
    161
    162
    163
    164
    165
    Exception in thread 
    "main" java.lang.OutOfMemoryError: Java heap space
        at java.util.ArrayList.
    <init>(Unknown Source)
        at TailRecursionTest2.a(TailRecursionTest2.java:
    17)
        at TailRecursionTest2.a(TailRecursionTest2.java:
    20)
        at TailRecursionTest2.a(TailRecursionTest2.java:
    20)
        at TailRecursionTest2.a(TailRecursionTest2.java:
    20)
        at TailRecursionTest2.a(TailRecursionTest2.java:
    20)
       我倒,才運行166次了,heap就滿了。問題在哪呢?oh,yep,你肯定想到了,是不是重復創建list這個大集合引起的呢?它不是局部變量嗎?怎么也會溢出?是的,list是局部變量,在a的方法棧里引用著,指向heap上的大對象,更關鍵的問題在于,java是沒有尾遞歸優化的,遞歸方法是不會使用同一個棧幀,每一次遞歸調用,都將壓入新的棧幀,并且這個棧幀上又new了一個list變量,引用著heap上新的一個大集合。隨著棧深度的增加,jvm里維持著一條長長的方法調用軌跡以便你能回來,在方法沒有返回之前,這些list變量一直被各自的棧幀引用著,不能被GC,你說,能不OOM嗎?
        也許,你想到了個補救方法來挽救程序2,就是每次在處理完list后,我把它設置為null,不讓棧幀繼續引用著它,咱編寫對gc友好的代碼,這不就行了,試試:
    import java.util.ArrayList;
    import java.util.List;

    public class TailRecursionTest2 {
        
    public static void main(String[] args) {
            TailRecursionTest2 t 
    = new TailRecursionTest2();
            t.a(
    0);
        }

        
    public void a(int j) {
            System.out.println(j);
            j
    ++;
            
    if (j == 10000)
                
    return;
            List list 
    = new ArrayList<Integer>(100000);
            
    // 對list進行處理
            list = null;  //gc友好
            a(j);
        }
    }
        得意洋洋,我跑一下看看,這次跑到4000多次,但是:
    ......
    4289

    4290
    4291
    4292
    java.lang.StackOverflowError
        at sun.nio.cs.ext.DoubleByteEncoder.encodeArrayLoop(Unknown Source)
        at sun.nio.cs.ext.DoubleByteEncoder.encodeLoop(Unknown Source)
        at java.nio.charset.CharsetEncoder.encode(Unknown Source)
        沒辦法啊,人家sun的jdk就是不支持尾遞歸優化,很不給你面子的棧溢出了。ibm的jdk據說支持尾遞歸優化,上面這個程序在ibm的jdk上可能可以正常結束,未經測試。

    總結:在java里,遞歸最好咱還是別用,老老實實地while、for;就算遞歸了,最好遞歸方法不要new太大的對象,除非你能確定遞歸的深度不是那么大。

    posted @ 2008-05-31 17:25 dennis 閱讀(2150) | 評論 (4)編輯 收藏

         摘要: 6到11章  閱讀全文

    posted @ 2008-05-28 23:07 dennis 閱讀(4085) | 評論 (0)編輯 收藏

         摘要: 《Logic Programming with Prolog》的讀書筆記,書其實一般,內容都是基礎性的語法和BIPs介紹,比較失望的是對Prolog的應用涉及很少。  閱讀全文

    posted @ 2008-05-28 23:05 dennis 閱讀(4062) | 評論 (0)編輯 收藏

        去年申請的rubyeye.net,年初到期了,忘了去續費。今天又去申請了兩個:rubyeye.net和zhuangxiaodan.cn。cn域名就是便宜,一塊錢一個:)。rubyeye.net申請了兩年,以免再次忘記續費,總計花費98塊,還是挺劃算的。兩個都設置了url轉發,現在可以通過 www.zhuangxiaodan.cn和www.rubyeye.net訪問我的blog啦。貌似rubyeye.net的還沒生效,明天應該可以了。前天同事推薦了個ror的虛擬主機,一年200塊,300M空間,10G/月的流量,挺實惠,就想著搞個typo弄上去,搬家過去??苫叵肫鹎懊鎺状伟峒业慕洑v,心里立馬打退堂鼓,盡管可以去搞個腳本導入這些日志,也是夠麻煩的,我還得去看下typo的數據表結構??磥磉€是老老實實呆在blogjava為妙。

    posted @ 2008-05-27 16:43 dennis 閱讀(665) | 評論 (3)編輯 收藏

        今天整理代碼,發現一個去年寫的簡單的工作流引擎,基于petri網(參考這里的筆記),實現了順序、并行、循環和選擇四種路由,資源也實現了人工驅動和定時、延遲時間驅動;目前只實現了將工作流數據保存在內存的版本,然后就換工作,折騰著就忘了這個事兒,本來是計劃加入數據庫存儲的。盡管只是個toy,可能對工作流感興趣,或者想自己實現一個玩玩的朋友有參考價值,放到了google code上,svn地址:
     http://insectworkflow.googlecode.com/svn/trunk/

        源碼中有在example包下給了個請假的例子,流程定義文件就是processes包下的leave.xml,實現大概是這么個流程:
    填寫假單-》提交假單-and-split節點-》項目經理審批-》and-join節點-》結束
                                                         -》部門經理審批-》

    其中項目經理審批和部門經理審批是并行路由。xml配置大概這樣:
    <node type="and-split" name="and-split" id="2">
            
    <inputs>
                
    <place id="3" />
            
    </inputs>
            
    <outputs>
                
    <place id="4" />
                
    <place id="5" />
            
    </outputs>
        
    </node>
        
    <node name="dept_manager_confirm" id="3">
            
    <resource class="com.google.code.insect.workflow.impl.Group" id="2"
                name
    ="dept_manager">
            
    </resource>
            
    <conditions type="and">
                
    <condition
                    
    class="com.google.code.insect.workflow.impl.NullHandler" value="false"
                    variable-name
    ="LeaveInfo" />
            
    </conditions>
            
    <handler
                
    class="com.google.code.insect.workflow.example.leave.SendRemindHandler" />
            
    <inputs>
                
    <place id="4" />
            
    </inputs>
            
    <outputs>
                
    <place id="6" />
            
    </outputs>
        
    </node>
        
    <node name="project_manager_confirm" id="4">
            
    <resource class="com.google.code.insect.workflow.impl.Group" id="3"
                name
    ="project_manager">
            
    </resource>
            
    <conditions type="and">
                
    <condition
                    
    class="com.google.code.insect.workflow.impl.NullHandler" value="false"
                    variable-name
    ="LeaveInfo" />
            
    </conditions>
            
    <handler
                
    class="com.google.code.insect.workflow.example.leave.SendRemindHandler" />
            
    <inputs>
                
    <place id="5" />
            
    </inputs>
            
    <outputs>
                
    <place id="7" />
            
    </outputs>
        
    </node>
        
    <node type="and-join" name="and-join" id="5">
            
    <handler
                
    class="com.google.code.insect.workflow.example.leave.ResultHandler" />
            
    <inputs>
                
    <place id="6" />
                
    <place id="7"></place>
            
    </inputs>
            
    <outputs>
                
    <place id="8" />
            
    </outputs>
        
    </node>

        其中的place就是各個Transition的輸入或者輸出庫所,所謂node其實就是變遷(transition),每個變遷對應一個handler,執行具體的業務操作,比如這里的com.google.code.insect.workflow.example.leave.SendRemindHandler 用于發送提醒消息給經理們。

        具體調用和工作項的人工觸發:

    //初始化工作流管理器
    WorkFlowManager wm = new BasicWorkflowManager();
    wm.setConfiguration(
    new DefaultConfiguration());

    //啟動一個案例
    Token token = wm.startWorkFlow("leave");
    token.setAttribute(
    "LeaveInfo", leaveInfo);

    //提交假單
    wm.doAction(token.getId(), this.dennis, "給領導發送消息:"
                    
    + leaveInfo.getStaff_name() + "申請請假,請批準!");
    //將token的id傳遞給后續節點做處理。。token的id就是案例id
        processes包下面的流程定義文件和test包下的TestUnit,分別測試了四種路由和定時、延時觸發,有興趣的可以看一下。

    posted @ 2008-05-21 15:09 dennis 閱讀(2146) | 評論 (0)編輯 收藏

        轉眼間,來廣州快半年了,感覺還不錯。廣州如死魚所說的那樣,是個包容并且很有活力的城市,習慣了周末煲湯,去天河公園跑跑步,這生活還是挺舒適的,除了比較潮的天氣。
        最近跟公司鬧了點不愉快,在轉正時間上,其實不是多大的事,只是心里不舒服罷了,干起活來也沒什么激情了,呵呵。當然,手頭的工作咱還是要高效率地完成,做完兩個游戲后,現在轉到棋牌類,棋牌類游戲核心就兩個算法:隨機發牌和出牌判斷。隨機發牌算法,學習了云風的blog上提到的方法,感覺還可以接受;出牌規則判斷,倒是沒想象中的復雜,建立牌型的OO模型,一切都很簡單了。另外一個發現,用jdk6跑jruby1.1.1,比用jdk5跑效率(包括內存和CPU)好上很多,例如我們一個游戲進程,在使用jdk5時,CPU穩定在15%,內存85M,而改用jdk6后,cpu降到了%5以下,內存也縮減到70多M。
        搞完了之后,時間有點空閑,就想學點新東西,最后選擇了Prolog。Yep,非常地有趣,真正的聲明式編程語言。Prolog本質上就是兩個東西:規則和事實,由事實和規則出發,Prolog的推理系統將回答你的查詢(query)。有點類似現在流行中的規則引擎的概念,在對效率不是很考慮的場景中,嵌入一個Prolog引擎做規則引擎完全是可以的,java中有個tuProlog項目,可以關注一下。然后就是一直在讀的sicp,延時求值模擬無窮級數實在是相當地cool,大開眼界。這兩天一直在理解continuation這個概念,小有所得。一個表達式的求值可以分為兩個階段:“What to evaluate?”和“What to do with the value”,“What to do with the value”就是計算的Continuation。例如,scheme求值下列表達式:
    (if (null? x) (quote ()) (cdr x))
    先求值表達式(null? x),(null? x)就是“What to evaluate”,當(null? x)求值后,需要根據這個值來決定是執行(quote ())還是(cdr x),這個根據值來決定的過程就是Continuation。如果在每次函數調用時,同時傳入當前的continuation,那么就完全可以不要堆棧。call/cc就提供了這樣的一個語法糖,call/cc全稱就是call-with-current-continuation,要求參數是一個過程,調用這個過程,并且向這個過程傳入當前的continuation(一般稱為k,kont,或者Ruby中一般是c,cont),這就是call/cc為我們做的。call/cc是實現Continuation的方式之一,coroutine/fiber/yield也是實現continuation的方式?!禩he Scheme Programming Language》給出的輕量級進程機制的例子比較有趣:
    (define lwp-list '())
    (define (lwp thunk)
      (set! lwp
    -list (append lwp-list (list thunk))))
    (define start
      (
    lambda()
        (let ((p (car lwp
    -list)))
          (set! lwp
    -list (cdr lwp-list))
          (p))))
    (define pause
      (
    lambda()
        (callcc (
    lambda(k) 
                   (lwp (
    lambda () (k #f)))
                   (start)))))
    (lwp (
    lambda () (let f () (display "h") (pause) (f))))
    (lwp (
    lambda () (let f () (display "e") (pause) (f))))
    (lwp (
    lambda () (let f () (display "y") (pause) (f))))
    (lwp (
    lambda () (let f () (display "!") (pause) (f))))
    (lwp (
    lambda () (let f () (newline) (pause) (f))))
    (start)
    實現了代碼級的進程調度。

    posted @ 2008-05-21 11:45 dennis 閱讀(1950) | 評論 (4)編輯 收藏

    僅列出標題
    共56頁: First 上一頁 25 26 27 28 29 30 31 32 33 下一頁 Last 
    主站蜘蛛池模板: 亚洲自偷自偷在线制服| 国产在线播放线91免费| 亚洲精品国产成人| 亚洲色欲久久久综合网 | 亚洲AV无码一区二区三区DV| 日韩a级毛片免费观看| 91香蕉成人免费网站| 久久成人免费电影| 中文字幕看片在线a免费| 猫咪免费观看人成网站在线| 亚洲色大成网站WWW国产| 1区1区3区4区产品亚洲| 亚洲精品色午夜无码专区日韩| 深夜国产福利99亚洲视频| 免费看AV毛片一区二区三区| 在线视频观看免费视频18| 99re热精品视频国产免费| 精品国产麻豆免费人成网站| 久久精品免费大片国产大片| 特级毛片全部免费播放| 爱情岛论坛免费视频| 国产亚洲男人的天堂在线观看 | 99爱在线精品视频免费观看9| 成人免费区一区二区三区 | 亚洲国产精品一区二区第一页免| 午夜一级毛片免费视频| 在线观看免费毛片| 精品国产免费观看一区| 青青青青青青久久久免费观看| 成人免费视频软件网站| 免费看片A级毛片免费看| 免费观看的av毛片的网站| 国产精品成人无码免费| 国产乱弄免费视频| 亚洲人成人网站在线观看| 亚洲裸男gv网站| 亚洲欧洲自拍拍偷午夜色无码| 亚洲AV无码码潮喷在线观看| 亚洲人成亚洲精品| 亚洲一区二区久久| 亚洲国产AV无码一区二区三区|