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

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

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

    莊周夢蝶

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

        sicp的習題3.22,也就是以消息傳遞的風格重新實現隊列,我的解答如下:

    (define (make-queue)
      (let ((front
    -ptr '())
            (rear-ptr '()))
      (define (set-front-ptr! ptr) (set! front-ptr ptr))
      (define (set
    -rear-ptr! ptr) (set! rear-ptr ptr))
      (define (empty
    -queue?) (null? front-ptr))
      (define (front
    -queue)
        (
    if (empty-queue?)
            (error 
    "FRONT called with an empty queue")
            (car front
    -ptr)))
      (define (insert
    -queue! item)
        (let ((new
    -pair (cons item '())))
          (cond ((empty-queue?)
                  (set
    -front-ptr! new-pair)
                  (set
    -rear-ptr! new-pair))
                (
    else
                   (set
    -cdr! rear-ptr new-pair)
                   (set
    -rear-ptr! new-pair)))))
      (define (delete
    -queue!)
          (cond ((empty
    -queue?)
                 (error 
    "DELETE! called with an empty queue" queue))
                (
    else
                   (set
    -front-ptr! (cdr front-ptr)))))
      (define (dispatch m)
        (cond ((eq? m 
    'front-queue) (front-queue))
              ((eq? m 'empty-queue?) (empty-queue?))
              ((eq? m 'insert-queue!) insert-queue!)
              ((eq? m 'delete-queue!) delete-queue!)
              (else
                 (error 
    "Unknow method" m))))
        dispatch))
    (define (front
    -queue z) (z 'front-queue))
    (define (empty-queue? z) (z 'empty-queue?))
    (define (insert-queue! z item) ((z 'insert-queue!) item))
    (define (delete-queue! z) ((z 'delete-queue!)))
       
        由此,我才知道自己竟然一直沒有想到,scheme完全可以模擬單向循環鏈表,整整第三章都在講引入賦值帶來的影響,而我卻視而不見。在引入了改變函數后,數據對象已經具有OO的性質,模擬鏈表、隊列、table都變的易如反掌。首先,模擬節點對象,節點是一個序對,包括當前節點編號和下一個節點:
    (define (make-node n next) (cons n next))
    (define (set
    -next-node! node next) (set-cdr! node next))
    (define (set
    -node-number! node n) (set-car! node n))
    (define (get
    -number node) (car node))
    (define (get
    -next-node node) (cdr node))

        有了節點,實現了下單向循環鏈表:
    (define (make-cycle-list n)
      (let ((head (make
    -node 1 '())))
        (define (make-list current i)
          (let ((next
    -node (make-node (+ i 1'())))
            (cond ((= i n) current)
                  (
    else
                    (set
    -next-node! current next-node)
                    (make
    -list next-node (+ i 1))))))
        (set
    -next-node! (make-list head 1) head) 
        head))

        make-cycle-list生成一個有N個元素的環形鏈表,比如(make-cycle-list 8)的結果如下
    #0=(1 2 3 4 5 6 7 8 . #0#)
        Drscheme形象地展示了這是一個循環的鏈表。那么約瑟夫環的問題就簡單了:
    (define (josephus-cycle n m)
      (let ((head (make
    -cycle-list n)))
        (define (josephus
    -iter prev current i)
          (let ((next
    -node (get-next-node current)))
           (cond ((eq? next
    -node current) (get-number current))
                 ((
    = 1 i)
                  (set
    -next-node! prev next-node)
                  (josephus
    -iter prev next-node m))
                 (
    else
                   (josephus
    -iter current next-node (- i 1))))))
        (josephus
    -iter head head m)))

        從head節點開始計數,每到m,就將當前節點刪除(通過將前一個節點的next-node設置為current的下一個節點),最后剩下的節點的編號就是答案。

    posted @ 2008-04-16 10:27 dennis 閱讀(628) | 評論 (0)編輯 收藏

    《王陽明大傳》序,作者:周月亮

    一生極重踐履的陽明,本身就象只鞋。這只鞋上插著高貴的權力意志的權杖。形成心學的倒T字型結構——不是十字架,也不是鉆不出地平線的大眾的正T字型。他的“致良知”工夫就是要你站在地平線上。然后腳不離地的無限的向上升華,把人拉成頂天立地的大寫的人。

    拔著頭發離地球的是阿Q,當縮頭烏龜的是假洋鬼子,只是鞋而無權杖的是讀書沒有悟道的士子。只耍權杖而不愿當鞋的是政治流氓——那個意志不是高貴的權力意志,只是反人道的獨裁欲望。

    陽明的心學是這樣一種生活方式:既生活在這里,又生活在別處!

    《明 史》陽明本傳只附了一個學生,既因為別的成了氣候的學生都有傳,還因為這個學生最能體現陽明學的“鞋”精神,他叫冀元亨,他因去過寧王府而被當成陽明通寧 王的證據給抓起來,在錦衣衛的監獄里受百般折磨,但他對人依然象春風一樣,感動得獄吏和獄友一個勁的哭,他把坐大獄當成了上學堂。所有的司法人員都以為 奇,問他夫人:“你丈夫秉持什么學術?”她說:“我丈夫的學問不出閫幃之間”。聞者皆驚愕不已。

    但是,人皆在閫幃之間,誰有這種境界、風范?只生活在這里,反而得不到這里;單生活在別處,自然更得不到這里。

    先作只鞋,再插上權杖,也不是陽明學的精神。那就是把鞋的大地性當成了手段,斷斷成不了圣雄,只能成為梟雄。

    再高貴的鞋,也是踩在腳下;但路也正在腳下。不能生活在別處的人的所謂腳下之路,只是不得不走的路;有生活在別處之權力意志的人才能“踐履”在希望的道路上。

    在比做什么事成什么人更哲學的語義上說:穿什么鞋走什么路。

    陽明這只鞋,至少有親在性、超越性、詩性、葆真性、有應必變的踐履性.....許多人最大的痛苦就是找不到一只合腳的鞋。陽明這只鞋可以叫真、善、忍;可以叫真、智、樂,叫六通四辟...

    致良知,就是要你找到可以上路的合腳的鞋。致者,找也。能否找到呢?就看你肯不肯去找——因為,它就在你自身「心即理」。陽明這樣解釋孔子說的上智下愚不移——不是不能移,只是不肯移。

    說無路可走的人,是沒有握住自家的權杖,把生命的舵送給了別人——那人哪怕是上帝也會變成魔鬼——上帝的真誠包含著上帝的欺騙。

    心學或曰陽明學并不給世人提供任何現成或統一的鞋,如果有那種鞋就是枷鎖和桎銬了,心學只是告訴人們:每個人都能找到自己的那雙合腳的上天堂的鞋——找這雙鞋的工夫與上天堂的工夫是同一個工夫。

    路在腳下,鞋在心中。你的任務是找與走,走著找,找著走,邊找邊走...

    這樣邊找邊走,就凸現出權杖的“權道”來——已發生語義轉換,這個權道的“權”是秤砣、以及因此衍生的權衡、權宜的那個權。對于人心來說,權,就是“感應之幾”,“幾”就是微妙的恰好,象秤砣一樣隨被秤之物的輕重而變動,找到那個應該的恰好。所謂道, 就是“體乎物之中以生天下之用者也”「王夫之《周易外傳》卷一」。權道就是追求“時中”即永遠恰當的人間至道。約略等于具體問題具體分析這個馬克思主義的活的靈魂。

    沒有這個權道,權杖只是個擺設,有了這個權道,權杖才能變成如意金箍棒,草鞋才能變成船,駛向理想的港灣。通權達變,是孔子認可的最高境界。不能通權達變就只能刻舟求劍、守株待兔...儒學在近代陷入困境就因為秉政的儒臣們失去了權道。

    這個權道就是在踐履精神上加上權變智慧——絕對不是無標準的變色龍、流氓。一講權變就滑向流氓,為杜絕流氓就割斷權道,都是找不到權道、反權道的表現。權,這個衡量萬物的標準,用陽明的話說就是良知。良知在你心中,不用到別處去找。

    所以,陽明這只鞋還帶著秤砣,是風鈴也是駝鈴。

    posted @ 2008-04-14 14:00 dennis 閱讀(511) | 評論 (0)編輯 收藏

    4、

        Tablelua的主要——實際上,也是唯一的——數據結構。Table不僅在語言中,同時也在語言的實現中扮演著重要角色。Effort spent on a good implementation of tables is rewarded in the language,because tables are used for several internal tasks, with no qualms about performance。這有助于保持實現的小巧。相反的,任何其他數據結構的機制的缺乏也為table的高效實現帶來了很大壓力

           Lua中的table是關聯數組,也就是可以用任何值做索引(除了nil),也可以持有任何值。另外,table是動態的,也就是說當加進數據的時候它們將增長(給迄今不存在的域賦值)而移除數據的時候將萎縮(給域賦nil值)。

    不像大多數其他腳本語言,lua沒有數組類型。數組被表示為以整數做鍵的table。用table作為數組對于語言是有益的。主要的(益處)顯而易見:lua并不需要操縱表和數組的兩套不同的運算符。另外,程序員不用對兩種實現做出艱難選擇。在lua中實現稀疏數組是輕而易舉的。例如,在Perl里面,如果你嘗試去跑$a[1000000000]=1 這樣的程序,你能跑出個內存溢出?。?/span>you can run out of memory),因為它觸發了一個10億個元素的數組的創建(譯注,Perl的哲學是:去除不必要的限制)。而等價的lua程序 a={[1000000000]=1},(只是)創建了有一個單獨的項的表(而已)。


    直到lua 4.0,table都是作為hash表嚴格地實現:所有的pair都被顯式地保存。Lua5.0引入了一個新算法來優化作為數組的table:它將以整數為鍵的pair不再是存儲鍵而是優化成存儲值在真正的數組中。更精確地說,在lua 5.0中,table被實現為一個混合數據結構:它們包括一個hash部分和一個數組部分。圖2展示了一個有"x" 9.3, 1 100,2 200, 3 300對子的表的一種可能結構。注意到數組部分在右邊,并沒有存儲整數的key。這個劃分僅僅是在一個低的實現層次進行的,訪問table仍然是透明的,甚至在虛擬機內部(訪問table)也是如此。Table根據內容自動并且動態地對兩個部分進行適配:數組部分試圖從1n的相應地存儲值,關聯非整數鍵或者整數鍵超過數組范圍的值被存儲在hash部分。

    當一個table需要增長時,lua重新計算hash部分和數組部分的大小。任一部分都可能是空的。重新計算后的數組大小是至少是當前使用的數組部分從1n的一半情況下的最大n值(原文:The computed size of the array part is the largest n such that at least half the slots between 1 and n are in use)(稀疏數組時避免浪費空間),并至少有一個(元素)處在n/2+1n的槽中(當n2整除時,避免n的這樣的數組大小)。計算完大小后,lua創建了“新”的部分并將“舊”的部分的元素重新插入到的“新”的部分。作為一個例子,假設a是一個空表;它的數組部分和hash部分的大小都是0。當我們執行a[1]=v時,這個表需要增長到足夠容納新的鍵。Lua將為新的數組部分的大小選擇n=1(帶有一個項1v)。hash部分仍然保持為空。

    這個混合的方案有兩個優點。首先,訪問以整數為鍵的值加快了,因為不再需要任何的散列行為。其次,也是最重要的,數組部分占用的內存大概是將數組部分存儲在hash部分時的一半,因為key在數組中是隱式的(譯注:也就是數組的下標)而在hash部分卻是顯式的。因而,當一個table被用作數組的時,它表現的就像一個數組,只要整數鍵是密集。另外,不用為hash部分付出任何內存和時間上的懲罰,因為它(譯注:hash部分)甚至都不存在。相反的控制:如果table被用作關聯數組而非一個數組,數組部分可能就是空的。這些內存上的節省是比較重要的,因為lua程序經常創建一些小的table,例如table被用來實現對象(Object)(譯注,也就是用table來模仿對象,有點類似javascript中的json)。

    Hash部分采用Brent's variation[3]的組合的鏈狀發散表。這些table的主要不變式是如果一個元素沒有在它的主要位置上(也就是hash值的原始位置),則沖突的元素在它自己的主要位置上。換句話說,僅當兩個元素有相同的主要位置(也就是在當時table大小情況下的hash值)時才有沖突的。沒有二級沖突。正因為那樣,這些table的加載因子可以是100%而沒有性能上的損失。這部分不是很明白,附上原文:

    The hash part uses a mix of chained scatter table with Brent's variation [3].

    A main invariant of these tables is that if an element is not in its main position

    (i.e., the original position given by its hash value), then the colliding element

    is in its own main position. In other words, there are collisions only when two

    elements have the same main position (i.e., the same hash values for that table

    size). There are no secondary collisions. Because of that, the load factor of these

    tables can be 100% without performance penalties.

     

     

    5、函數和閉包

    lua編譯一個函數的時候,它產生一個模型(prototype),包括了函數的虛擬機指令、常量值(數字,字符串字面量等)和一些調試信息。運行的時候,無論何時lua執行一個function…end表達式,它都創建一個新的閉包。每個閉包都有一個引用指向相應的模型,一個引用指向環境(environment)(一張查找全局變量的表,譯注:指所謂環境就是這樣一張表),和一組用來訪問外部local變量的指向upvalue的引用。

    詞法范圍以及first-class函數的組合導致一個關于(如何)訪問外部local變量的著名難題。考慮圖3中的例子。當add2被調用的時候,它的函數體(body)部分引用了外部local變量x (函數參數在lua里是local變量,譯注:x就是所謂的自由變量,這里形成了閉包)。盡管如此,當add2被調用時,生成add2的函數add已經返回。如果在棧中生成變量x,(x在棧的)其棧槽將不復存在。(譯注,此處的意思應該是說如果在棧保存變量x,那么在調用add2的時候,add函數早已經返回,x也在add2調用前就不在棧里頭了,這就是那個著名難題)。


     

     

    大多數過程語言通過限制詞法范圍(例如python),不提供first-class函數(例如Pascal),或者都兩者都采用(例如c,譯注:也就是說c既不把函數當一等公民,也限制詞法范圍)來回避這個問題。函數式語言就沒有那些限制。圍繞著非純粹函數語言比如SchemeML的研究已經產生了大量的關于閉包的編譯技術的知識(參見[19, 1, 21])。盡管如此,這些工作并沒有盡力去限制編譯器的復雜性。以優化的Scheme編譯器Bigloo的控制流分


    析階段為例,(它的實現)比lua實現的10倍還大:Bigloo 2.6fCfa模塊源碼有106,350 VS. 10,155行的lua5.0核心。正如第2部分已經解釋過的(原因),lua需要簡單。

    Lua使用了一個稱為upvalue的結構來實現閉包。任何外部local變量的訪問都是通過一個upvalue間接進行的。Upvalue初始指向的是變量存活位置的棧槽(參見圖4的左半部分)。當變量(x)已經離開作用域(譯注,也就是這里的add函數返回時),它就遷移到upvalue結構本身一個槽中(參見圖4的右半部分)。因為(對變量的)訪問是間接地通過upvalue結構中的一個指針進行的,因此這個遷移對于任何寫或者讀該變量的代碼都是透明的。不像它的內嵌函數(譯注:例子的add2,它是指外部函數add),聲明變量的函數訪問該變量就像訪問它的local變量一樣:直接到棧。

    通過每個變量最多創建一個upvalue結構并且在必要的時候復用它們,可變狀態得以在閉包之間正確地共享。為了保證這一約束,lua維持一個鏈表,里面是一個棧里(圖4中的pending vars列表)的所有open upvalue(所謂open upvalue,是指仍然指向棧的upvalue結構)。當lua創建一個新的閉包的時候,它遍歷所有的外部local變量。對于每個(外部local)變量,如果它在列表中找到一個open upvalue,那么它就復用這個upvalue結構。否則,lua就創建一個新的upvalue并將它放入鏈表。注意到列表搜索只是探測了少數幾個節點,因為列表最多包含一個被內嵌函數使用的local變量的項。當一個closed upvalue不再被任何閉包引用的時候,它最后將被當作垃圾并回收。

    一個函數允許訪問一個不是它的直接外圍函數的外部local變量,只要(這個local變量)是外部函數的(outer function)。在那種情況下,甚至在閉包被創建的時候,(外部local)變量可能就不在棧里了。Lua使用扁平閉包(flat closures)解決這種情況[5]。通過扁平閉包,一個函數無論何時去訪問一個不屬于它的外圍函數的外部變量,這個變量都將進入外圍函數的閉包。從而當一個函數被實例化的時候,所有進入它閉包的變量要么在外圍函數的棧里面,要么在外圍函數的閉包里。

    posted @ 2008-04-07 17:55 dennis 閱讀(3117) | 評論 (0)編輯 收藏

    三個多月前翻譯的,今天又找出來看看,后面的待整理下繼續發,有錯誤的地方請不吝賜教。

    原文:http://www.tecgraf.puc-rio.br/~lhf/ftp/doc/jucs05.pdf

    翻譯:dennis zhuang (killme2008@gmail.com)  http://www.tkk7.com/killme2008

    轉載請注明出處,謝謝。

     

    摘要:我們討論了lua 5.0實現的主要新特性:基于寄存器的虛擬機,優化表的新算法以便(將表)用作數組,閉包的實現,以及coroutines(譯注:協程)

    關鍵字: compilers, virtual machines, hash tables, closures, coroutines

     

    1.    介紹

    Lua作為內部使用的開發工具誕生于學術實驗室中,現在卻已經被世界范圍內許多工業級項目所采用,廣泛應用于游戲領域。Lua為什么能獲得這樣廣泛的應用呢?我們認為答案就來源于我們的設計和實現目標上:提供一種簡單、高效、可移植和輕量級的嵌入式腳本語言。這是Lua1993年誕生以來我們一直追求的目標,并在(語言的)演化過程中遵守。這些特性,以及Lua一開始就被設計成嵌入大型應用的事實,才使它在早期被工業界所接受。

     

    廣泛的應用產生了對(新的)語言的特性的需求。Lua的許多特性來自于工業需求和用戶反饋的推動。重要的例如lua 5.0引入的coroutines和即將到來的Lua 5.1改進的垃圾收集實現,這些特性對于游戲(編程)特別重要。

     

    在這篇論文中,我們討論了lua 5.0相比于lua 4.0的主要新特性:

    基于寄存器的的虛擬機:傳統上,絕大多數虛擬機的實際執行都是基于棧,這個趨勢開始于PascalPmachine,延續到今天的java虛擬機和微軟的.net環境。目前,盡管對于基于寄存器的虛擬機的興趣逐漸增多(比如Perl6計劃中的新的虛擬機(Parrot)將是基于寄存器的),但是就我們所知,lua 5.0是第一個被廣泛使用的基于寄存器的虛擬機。我們將在第7部分描述這個虛擬機。

     

    優化表的新算法以便作為數組: 不像其他腳本語言,Lua并沒有提供數組類型。Lua使用整數索引的普通表來實現數組作為替代。Lua 5.0使用了一個新的算法,可以檢測表是否被作為數組使用,并且可以自動將關聯著數字索引的值存儲進一個真實的數組,而不是將它們放進Hash表。在第4部分我們將討論這個算法。

     

    閉包的實現:lua 5.0在詞法層次上支持first-class 函數(譯注:將函數作為一等公民)。這個機制導致一個著名的語言難題:使用基于數組的棧來存儲激活記錄。Lua 使用了一個新辦法來實現函數閉包,保存局部變量在(基于數組)的棧(stack)上,當它們被內嵌函數引用而從作用域逸出的時候才將它們轉移到堆(heap)上。閉包的實現將在第5部分討論。

    添加coroutines lua 5.0語言引入了coroutines。盡管coroutines的實現較為傳統,但為了完整性我們將在第6部分做個簡短的概況介紹。

     

    其他部分是為了討論的完整性或者提供背景資料。在第2部分我們介紹了lua的設計目標以及這個目標如何驅動實現的概況。在第3部分我們介紹了lua是如何表示值的。盡管就這個過程本身沒有什么新意,但是為了(理解)其他部分我們需要這些資料。最后,在第8部分,我們介紹了一個小型的基準測試來得到一些結論。

     

    2.    lua設計和實現概況

    在介紹部分提到過的,lua實現的主要目標是:

     

    簡單性:我們探索我們能提供的最簡單的語言,以及實現(這樣的)語言的最簡單的C代碼。這就意味著(需要)不會偏離傳統很遠的擁有很少語言結構的簡單語法。

     

    效率:我們探索編譯和執行lua程序的最快方法,這就意味著(需要)一個高效的、聰明的一遍掃描編譯器和一個高效的虛擬機。

     

    可移植性:我們希望lua能跑在盡可能多的平臺上。我們希望能在任何地方不用修改地編譯lua核心,在任何一個帶有合適的lua解釋器的平臺上不用修改地運行lua程序。這就意味著一個對可移植性特別關注的干凈的ANSI C的實現,例如避開C和標準庫庫中的陷阱缺陷,并確保能以c++方式干凈地編譯。我們追求warning-free的編譯(實現)。

     

    嵌入性:lua是一門擴展語言,它被設計用來為大型程序提供腳本設施。這個以及其他目標就意味著一個簡單并且強大的C API實現,但這樣將更多地依賴內建的C類型。

     

    嵌入的低成本:我們希望能容易地將Lua添加進一個應用,而不會使應用變的臃腫。這就意味著(需要)緊湊的C代碼和一個小的Lua核心,擴展將作為用戶庫來添加。

     

    這些目標是有所權衡的。例如,lua經常被用作數據描述語言,用于保存和加載文件,有時是非常大的數據庫(M字節的lua程序不常見)。這就意味著我們需要一個快速的lua編譯器。另一方面,我們想讓lua程序運行快速,這就意味著(需要)一個可以為虛擬機產生優秀代碼的聰明的編譯器。因此,LUA編譯器的實現必須在這兩種需求中尋找平衡。盡管如此,編譯器還是不能太大,否則將使整個發行包變的臃腫。目前編譯器大約占lua核心大小的30%。在內存受限的應用中,比如嵌入式系統,嵌入不帶有編譯器的Lua是可能的,Lua程序將被離線預編譯,然后被一個小模塊(這個小模塊也是快速的,因為它加載的是二進制文件)在運行時加載。

     

    Lua使用了一個手寫的掃描器和一個手寫的遞歸下降解釋器。直到3.0版本,lua還在使用一個YACC產生的解釋器,這在語言的語法不夠穩定的時候很有價值的。然而,手寫的解釋器更小、更高效、更輕便以及完全可重入,也能提供更好的出錯信息(error message)。

    Lua編譯器沒有使用中間代碼表示(譯注:也就是不生成中間代碼)。當解釋一個程序的時候,它以“on-the-fly”的方式給虛擬機發出指令。不過,它會進行一些優化。例如,它會推遲像變量和常量這樣的基本表達式的代碼生成。當它解釋這樣的表達式的時候,沒有產生任何代碼,而是使用一種簡單的結構來表示它們。所以,判斷一個給定指令的操作數是常量還是變量以及將它們的值直接應用在指令都變的非常容易,避免了不必要的和昂貴的移動。

    為了輕便地在許許多多不同的C編譯器和平臺之間移植,Lua不能使用許多解釋器通常使用的技巧,例如direct threaded code [8, 16]。作為替代,它(譯注:指lua解釋器)使用了標準的while-switch分發循環。此處的C代碼看起來過于復雜,但是復雜性也是為了確??梢浦残?。當lua在許多不同的平臺上(包括64位平臺和一些16位平臺)被很多不同的C編譯器編譯,lua實現的可移植性一直以來變的越來越穩定了。

    我們認為我們已經達到我們的設計和實現目標了。Lua是一門非常輕便的語言,它能跑在任何一個帶有ANSI C編譯器的平臺上,從嵌入式系統到大型機。Lua確實是輕量級的:例如,它在linux平臺上的獨立解釋器包括所有的標準庫,占用的空間小于150K;核心更是小于100Klua是高效的:獨立的基準測試表明lua是腳本語言(解釋的、動態類型的語言)領域中最快的語言之一。主觀上我們也認為lua是一門簡單的語言,語法上類似Pascal,語義上類似Scheme(譯注:Lisp的一種方言)。

     

    3、值的表示

    Lua是動態類型語言:類型依附于值而不是變量。Lua8種基本類型:nil, boolean, number, string, table, function,userdata, thread。Nil是一個標記類型,它只擁有一個值也叫nil。Boolean就是通常的truefalse。Number是雙精度浮點數,對應于C語言中的double類型,但用float或者long作為替代來編譯lua也很容易(不少游戲consoles或者小機器都缺乏對double的硬件支持)String是有顯式大小的字節數組,因此可以存儲任意的二進制類型,包括嵌入零。Table類型就是關聯的數組,可以用任何值做索引(除了nil),也可以持有任何值。Function是依據與lua虛擬機連接的協議編寫的lua函數或者C函數。Userdata本質上是指向用戶內存區塊(user memory block)的指針,有兩種風格:重量級,lua分配塊并由GC回收;輕量級,由用戶分配和釋放(內存)塊。最后,thread表示coroutines。所有類型的值都是first-class值:我們可以將它們作為全局變量、局部變量和table的域來存儲,作為參數傳遞給函數,作為函數的返回值等等。

     

    typedef struct {                        typedef union {

    int t;                                      GCObject *gc;

    Value v;                                   void *p;

    } TObject;                                 lua_Number n;

    int b;

    } Value;

    Figure 1: 帶標簽的union表示lua

     

     

    Lua將值表示為帶標簽的union(tagged unions),也就是pairs(t,v),其中t是一個決定了值v類型的整數型標簽,而v是一個實現了lua類型的C語言的union結構。Nil擁有一個單獨的值(譯注:也就是nil)。Booleansnumbers被實現為“拆箱式”的值:vunion中直接表示這些類型的值。這就意味著union(譯注:指圖中的Value)必須有足夠的空間容納double(類型)。Strings,tables, functions, threads, userdata類型的值通過引用來實現:v擁有指向實現這些類型的結構的指針。這些結構(譯注:指實現Strings,tables, functions, threads, userdata這些類型的具體結構)共享一個共同的head,用來保存用于垃圾收集的信息。結構的剩下的部分專屬于各自的類型。

     Figure1展示了一個實際的lua值的實現。TObject是這個實現的主要結構體:它表示了上文描述的帶標簽的聯合體(tagged unions (t,v)。Value是實現了值的union類型。類型nil的值沒有顯式表示在這個union類型中是因為標簽已經足夠標識它們。域n用來表示numbers類型(lua_Number默認是double類型)。同樣,域b是給booleans類型用的,域p是給輕量級的userdata類型。而域gc是為會被垃圾回收的其他類型準備的((strings, tables, functions, 重量級userdata, threads)。

     

    使用帶標簽的union實現lua值的一個后果就是拷貝值的代價稍微昂貴了一點:在一臺支持64double類型的32位機器上,TObject的大小是12字節(或者16字節,如果double8字節對齊的話),因此拷貝一個值將需要拷貝3(或者4)個機器字長。盡管如此,想在ANSI C中實現一個更好的值的表示是困難的。一些動態類型語言(例如Smalltalk80的原始實現)在每個指針中使用多余的位來存儲值的類型標簽。這個技巧在絕大多數機器上正常工作,這是因為一個指針的最后兩個或者三個位由于對齊將總是0,所以可以被用作他途。但是,這項技術既不是可移植的也無法在ANSI C中實現,C 語言標準甚至都不保證指針適合任何整數類型,所以沒有在指針上操作位的標準方法。

    減小值大小的另一個觀點就是持有顯式標簽,從而避免在union中放置一個double類型。例如,所有的number類型可以表示為堆分配的對象,就像String那樣。(python使用了這項技術,除了預先分配了一些小的整數值)。盡管如此,這樣的表示方法將使語言變的非常緩慢。作為選擇,整數的值可以表示位“拆箱式”的值,直接存儲在union中,而浮點值放在堆中。這個辦法將極大地增加所有算術運算操作的實現復雜度。

    類似早期的解釋型語言,例如Snobol [11] Icon [10]lua在一個hash表中“拘留”(internalizes)字符串:(hash表)沒有重復地持有每個字符串的單獨拷貝。此外,String是不可變的:一個字符串一旦被“拘留”,將不能再被改變。字符串的哈希值依據一個混合了位和算術運算的簡單表達式來計算,囊括所有的位。當字符串被“拘留”時,hash值保存(到hash表),以支持更快的字符串比較和表索引。如果字符串太長的話,hash函數并不會用到字符串的所有字節,這有利于快速地散列長字符串。避免處理長字符串帶來的性能損失是重要的,因為(這樣的操作)在lua中是很普遍的。例如,用lua處理文件的時候經常將整個文件內容作為一個單獨的長字符串讀入內存。

    posted @ 2008-04-07 17:25 dennis 閱讀(2701) | 評論 (2)編輯 收藏

        模塊化的價值毋庸置疑。
        模塊化代碼的首要特質就是封裝。封裝良好的模塊不會過多向外部披露自身的細節,不會直接調用其它模塊的實現碼,也不會胡亂共享全局數據。模塊之間通過應用程序編程接口(API)——一組嚴密、定義良好的程序調用和數據結構來通信。這就是模塊化原則的內容。API在模塊間扮演雙重角色。在實現層面,作為模塊之間的滯塞點(choke point),阻止各自的內部細節被相鄰模塊知曉;在設計層面,正是API(而不是模塊間的實現代碼)真正定義了整個體系。
        模塊的最佳模塊大小,邏輯行在200到400之間,物理行在400到800之間為最佳。模塊太小,幾乎所有的復雜度都集中在接口,同樣不利于理解,也就是透明性的欠缺。
       緊湊性就是一個特性能否裝進人腦中的特性。理解緊湊性可以從它的“反面”來理解,緊湊性不等于“薄弱”,如果一個設計構建在易于理解且利于組合的抽象概念上,則這個系統能在具有非常強大、靈活的功能的同時保持緊湊。緊湊也不等同于“容易學習”:對于某些緊湊 設計而言,在掌握其精妙的內在基礎概念模型之前,要理解這個設計相當困難;但一旦理解了這個概念模型,整個視角就會改變,緊湊的奧妙也就十分簡單了。緊湊也不意味著“小巧”。即使一個設計良好的系統,對有經驗的用戶來說沒什么特異之處、“一眼”就能看懂,但仍然可能包含很多部分。
        評測一個API緊湊性的經驗法則是:API的入口點通常在7個左右,或者按《代碼大全2》的說法,7+2和7-2的范圍內。
        如果兩個或更多事物中的一個發生變化,不會影響其他事物,這些事物就是正交的。每一個動作只改變一件事,不會影響其他(沒有其他副作用)。舉個例子,比如讀取配置文件,獲得系統設置信息這個方法:
    module Config
       
    def self.load_config
             config_str
    =File.open("r","config.xml").read
             
    #解析配置文件,可能轉化成XML Dom處理等
             
       end
    end
    這個方法想當然地認為配置文件存儲于磁盤文件中,然而配置文件完全是有可能通過網絡獲取的,也就是說文件句柄未必來源于磁盤文件,這個方法承擔了兩個職責:獲取配置數據和解析配置數據。重構一下,以提高正交性:
    module Config
       
    def self.load_config(io)
             config_str
    =io.read
             parse_config(config_str)         
       end
       private
       
    def self.parse_config(config_str)
        
    #解析
       end 
    end
        重構技術中的很多壞味道,特別是重復代碼,是違反正交性的明顯例子,“重構的原則性目標就是提高正交性”。
        DRY(Don't Repeat Yourself)原則,意思是說:任何一個知識點在系統內都應當有一個唯一、明確、權威的表述。這個原則的另一種表述就是所謂SPOT原則(Single Point Of Truth)——真理的單點性。
        提高設計的緊湊性,有一個精妙但強大的方法,就是圍繞“解決一個定義明確的問題”的強核心算法組織設計,避免臆斷和捏造。

    posted @ 2008-04-06 13:00 dennis 閱讀(1575) | 評論 (0)編輯 收藏

                         無善無惡心之體,
                         有善有惡意之動。
                         知善知惡是良知,
                         為善去惡是格物。

    這就是心學四訣。王陽明這樣的人物, 還是當年明月評價的妙:
    王守仁是一個高尚的人,一個純粹的人,一個有道德的人,一個脫離了低級趣味的人,一個有益于人民的人。他是真正的圣賢,當之無愧

    讀《明朝那些事兒》到此,慢慢了解這么多不一般的人:鐵鉉、于謙、李賢、王守仁......,甚至可嘆可笑可氣的正德帝,在當年明月的筆下栩栩如生、有聲有色。歷史不僅僅充斥了陰謀詭計、權利傾軋,原來也有傳奇的朱祁鎮與錢皇后的感人愛情,有王守仁的龍場悟道,有俗套到不能俗套地不斷上演火燒連環船,有正德帝這樣的快樂皇帝,有李景隆這樣難得一見的“人才”......不一樣的讀史,不一般的推薦。

    posted @ 2008-04-03 18:37 dennis 閱讀(870) | 評論 (4)編輯 收藏

        編程珠璣Column 4關于binary search的部分相當精彩,1946年就有人發表binary search,但直到1962第一個正確運行的算法才寫出來。盡管算法描述看起來簡單明了,但是要寫的正確卻是有許多地方要仔細考慮。作者著重強調的是對程序正確性的分析方法,仔細分析方法的pre-condition, invariant,和post-condition。g9老大翻譯過Joshua Bloch在google blog上的文章《號外,號外,幾乎所有的binary search和mergsort都有錯》,原文在這里。今天看了下原文,有更新,對于求中值索引的c/c++方法原文仍然是有錯的,本來是這樣:

    int mid = ((unsigned) (low + high)) >> 1

    但是在c99標準中,對于兩個signed數值之和,如果溢出結果是未定義的(undefined),也就是說與編譯器實現相關。上面的代碼應該修改為:

    mid = ((unsigned int)low + (unsigned int)high)) >> 1;

    我查了下jdk6的java.util.Arrays的源碼,joshua bloch說的這個問題已經解決,現在的binary search如下:

      // Like public version, but without range checks.
        private static int binarySearch0(int[] a, int fromIndex, int toIndex,
                         
    int key) {
        
    int low = fromIndex;
        
    int high = toIndex - 1;

        
    while (low <= high) {
            
    int mid = (low + high) >>> 1;
            
    int midVal = a[mid];

            
    if (midVal < key)
            low 
    = mid + 1;
            
    else if (midVal > key)
            high 
    = mid - 1;
            
    else
            
    return mid; // key found
        }
        
    return -(low + 1);  // key not found.
        }

        如原文所討論的,采用了>>>操作符替代除以2

    posted @ 2008-04-02 10:08 dennis 閱讀(2014) | 評論 (2)編輯 收藏

        《代碼大全2》剛出來的時候,鋪天蓋地的宣傳,好像《程序員》上也做了整整一期的專題。不能免俗,我也是很早就買了。剛買來沒有放進自己的讀書計劃,因為我的待讀書單已經很長了。翻讀過前面幾章,對使用隱喻去理解軟件開發一章印象深刻,還特意寫了blog。然后這本書就被我放在了老家,直到春節后被我帶到廣州,讀到昨天晚上,終于算是讀完了。
        這本書正如作者所說,是一本關于軟件構建的百科全書,小到一條注釋語句的編寫,大到高層設計和架構,從項目的需求分析到驗收測試,從方法論到實踐細節,從數據到案例,作者提供了全景式的軟件構建視角,并且對每一個主題都做到能有價值地探討。更可貴的是書中還提供了一張一張的核對表,讓你能心中有數地去實踐。
        感覺自己的語言是越來越貧乏了,呵呵,除了說好,好像不知道該怎么說了。看看過去在榕樹下的寫的那些“文章”,真不知道怎么就沒有了過去的那種感覺了?難道做技術工作就真的變的越來越枯燥?工作倒沒啥,一個游戲搞定了,馬上又一個立項,開始慢慢輕車熟路,只是與我來廣州的初衷相去甚遠咯,俺還是暫時成了Ruby程序員了。
        補充一個關于西藏的資料鏈接 https://docs.google.com/View?docid=dggh5mp6_73fvdxt4c9,各方觀點,精彩紛呈。屁股決定腦袋,至理名言吶。

    posted @ 2008-03-31 18:27 dennis 閱讀(657) | 評論 (1)編輯 收藏

        數據都是在我的機器上測試所得,我的機器配置:AMD athlon 64 x2 Dual 4000+ 2.11Ghz,1.87G內存。cruby版本是1.8.6,jruby是1.1RC3。操作系統是xp sp2。

    1、將繁忙的循環放在內層,比如下面的代碼:
    a=
    for i in 0..1000
      
    for j in 0..10
         a
    +=(i+j)
      end
    end
    替換成:
    for j in 0..10
       
    for i in 0..1000
        a
    +=(i+j)
       end
    end
    cruby提升15%左右,而jruby提升30%以上。

    2、乘法運算換成冪運算,cruby降低了200%以上,jruby僅降低30%。也就是說冪運算盡量換算成乘法運算。結論:冪運算換成乘法運算,乘法運算換成加法運算,多數情況下都能對性能有所提高,但請以實際測量為準。

    3、100*2 替換成 100<<1,jruby提升8左右%,c ruby提升在0到3%左右。結論:乘以2或者除以2操作可以替換成位移操作,這個調整對性能提高有限,可能以降低代碼可讀性為代價。

    4、字符串累積 a+="abc" 替換成 a<<"abc" c ruby提升接近100%,jruby提升97%

    5、將case...when...end語句替換成if...elsif...end語句,cruby沒有明顯變化(甚至有所降低),而jruby卻提高15%左右。同時,jruby的case...when...end語句的效率比cruby快上60%,if...elsif...end語句比cruby快上50%。結論:使用cruby,用case...when語句為好,而jruby則盡量使用if...elsif

    6、將case語句中的頻率比較高的分支提前,cruby提升15%左右,jruby也是如此。將if...elsif...end語句中的頻率比較高的部分提前,jruby提升比較少,大概在5%左右,而cruby可以達到10%。。結論:盡管頻率高的語句提前,可以適當提升性能,但可以看到也是有限的,分支語句的順序不能僅僅考慮頻率,更應該兼顧邏輯,維持在同一個抽象層次上。

    7、任何一次代碼調整,請都要測量一下,這些Tip僅僅是我在我的機器環境下的測試結果。這些調整策略對其他語言也大多有效,我是在讀了《代碼大全2》代碼調整一章后做的測試,并應用到我的代碼中了。

    posted @ 2008-03-27 09:46 dennis 閱讀(1811) | 評論 (2)編輯 收藏

    為了我家小白和小紅,特意查了下,原來養金魚也是要講究的:

    1、注意放養方法

    剛買回來的金魚,切忌連魚帶水一起倒入魚缸。因為剛買回來的魚,在塑料袋中的水溫與家中魚缸中的水溫不等,往往會使魚“感冒”患病。有時買回來的魚和水帶有病菌,一旦倒入魚缸中,反而連累其他魚。所以,剛買回來的魚應該先連同塑料袋一起在空缸水中放置20分鐘,讓袋內外水溫達到一致時,再把魚輕輕倒入空面盆中,適當加入少許等溫新水,靜止半小時,把魚撈入盛有新水的空魚缸中飼養。過一周后,未發現魚兒有病,并已恢復正?;顒訒r,再把魚撈入飼養缸中合并飼養。

    2、注意魚缸消毒

    魚缸的消毒可用高錳酸鉀溶液,以淡紫色高錳酸鉀溶液浸泡一段時間即可。消毒以后再用清水清洗兩遍。如果是用黏合劑黏結的玻璃魚缸,最好放置一段時間后再用,因為化學黏結劑對金魚有損傷。

    3、注意光照

    金魚應在光亮、黑暗交替環境下生存。光照既可以增氧,又可以刺激魚體表色素細胞,有利于加快變色和出現鮮艷光澤。

    4、注意投喂方法

    金魚是觀賞魚,養魚的目的不是要它很快長大,所以也要控制投食量,尤其是新買來以后,投食量過多往往把金魚撐死。剛買回來的金魚一時不適應新環境的改變,故開始的1~2天內不要給食,第三天才可給以少量食物,給食量只能逐漸增加,投食量不超過魚體質量的1%。以后按此比例兩天投食一次即可,切不要喂食量過多過勤。喂多了反到以下壞處:一是魚吃飽了,代謝水平提高,耗氧量增加,容易引起缺氧窒息而死亡;二是餌料剩下,容易腐敗發酵,使水質變壞,也會造成缺氧。其實金魚是比較耐餓的,一兩周不喂,也不會發生問題。

    5、注意換水量及換水方法;

    金魚屬冷血動物,能在0~39℃之間正常生存,最適宜溫度是20~28℃。換水時溫差不宜超過2℃,溫差超過7℃時易得病,甚至死亡。另外,金魚尤其是幼魚比較嬌嫩,不宜徹底換水。①夏季1~2天部分換水一次,10~15天全部換水一次,冬季一般不換水。②換水宜少不宜多(換水量為總量的1/4~1/3)。在換水時,一定要使用新(優)水,水溫差不宜過大,若用自來水,使用前必須涼曬1~2天再用,或每立方米水體中加入2~3克大蘇打,以中和水體中的氯離子。若用井水,用淺井水養金魚效果更好,但使用前也須1~2天再使用。若用江、河、湖水,一定是未污染的,可直接用來養金魚,但水中腐殖質及寄生蟲容易危害金魚,因而經過濾后使用較安全。

    換水的方法:宜在傍晚四、五點鐘進行,用乳膠吸管輕輕吸去缸底的糞便和剩余的飼料等污物和部分老水,兌入等溫新水,在注入新水時,應按量慢慢地沿缸壁注入,切忌猛倒、猛沖,更不要沖及魚體,否則容易使金魚著涼內傷,引發爛鱗病和毛細血管充血等疾病。等魚適應環境后,再轉入正常飼養管理。

    posted @ 2008-03-26 15:18 dennis 閱讀(7840) | 評論 (5)編輯 收藏

    僅列出標題
    共56頁: First 上一頁 27 28 29 30 31 32 33 34 35 下一頁 Last 
    主站蜘蛛池模板: 成人亚洲国产va天堂| 国产乱子伦精品免费无码专区| 亚洲性一级理论片在线观看| 天天摸天天碰成人免费视频| 日本精品久久久久久久久免费| 亚洲av色福利天堂| 岛国精品一区免费视频在线观看| yy6080久久亚洲精品| 免费一级毛片无毒不卡| 一本色道久久88—综合亚洲精品| 亚洲免费中文字幕| 美女羞羞视频免费网站| 国产一级大片免费看| 久久精品国产免费| 久久精品国产亚洲AV电影网| 成人毛片视频免费网站观看| 亚洲另类无码一区二区三区| 免费理论片51人人看电影| 岛国岛国免费V片在线观看| 亚洲精品无码成人片久久不卡| 国产偷v国产偷v亚洲高清| 午夜视频在线免费观看| 亚洲国产日韩一区高清在线 | 国产精品亚洲综合网站| 免费在线观看日韩| 最近中文字幕mv免费高清在线| 男女交性无遮挡免费视频| 亚洲a级成人片在线观看| 成人免费无遮挡无码黄漫视频| 成全视频高清免费观看电视剧| 日韩色视频一区二区三区亚洲 | 亚洲精品日韩专区silk| 国产精品成人观看视频免费| 国产va免费精品| 成人婷婷网色偷偷亚洲男人的天堂| 亚洲日韩中文字幕| 情人伊人久久综合亚洲| 免费h片在线观看网址最新| 好湿好大好紧好爽免费视频 | 一级特黄aa毛片免费观看| 成全视成人免费观看在线看|