2012-05-20 47 views
18

我在網上發現了這個代碼,它在程序中有一個程序。 我不明白爲什麼作者會選擇這樣寫。 我注意到的是正在執行的遞歸函數。程序中的程序?

爲什麼他沒有像我見過的大多數代碼那樣分開程序。

他實現:

procedure XML2Form(tree : TJvPageListTreeView; XMLDoc : TXMLDocument); 
var 
    iNode : IXMLNode; 

    procedure ProcessNode(
    Node : IXMLNode; 
    tn : TTreeNode); 
    var 
    cNode : IXMLNode; 
    begin 
    if Node = nil then Exit; 
    with Node do 
    begin 
     tn := tree.Items.AddChild(tn, Attributes['text']); 
     tn.ImageIndex := Integer(Attributes['imageIndex']); 
     tn.StateIndex := Integer(Attributes['stateIndex']); 
    end; 

    cNode := Node.ChildNodes.First; 
    while cNode <> nil do 
    begin 
     ProcessNode(cNode, tn); 
     cNode := cNode.NextSibling; 
    end; 
    end; (*ProcessNode*) 
begin 
    tree.Items.Clear; 
    XMLDoc.FileName := ChangeFileExt(ParamStr(0),'.XML'); 
    XMLDoc.Active := True; 

    iNode := XMLDoc.DocumentElement.ChildNodes.First; 

    while iNode <> nil do 
    begin 
    ProcessNode(iNode,nil); 
    iNode := iNode.NextSibling; 
    end; 
    XMLDoc.Active := False; 
end; (* XML2Form *) 


procedure Form2XML(tree: TJVPageListTreeView); 
var 
    tn : TTreeNode; 
    XMLDoc : TXMLDocument; 
    iNode : IXMLNode; 

    procedure ProcessTreeItem(
    tn : TTreeNode; 
    iNode : IXMLNode); 
    var 
    cNode : IXMLNode; 
    begin 
    if (tn = nil) then Exit; 
    cNode := iNode.AddChild('item'); 
    cNode.Attributes['text'] := tn.Text; 
    cNode.Attributes['imageIndex'] := tn.ImageIndex; 
    cNode.Attributes['stateIndex'] := tn.StateIndex; 
    cNode.Attributes['selectedIndex'] := tn.SelectedIndex; 

    //child nodes 
    tn := tn.getFirstChild; 
    while tn <> nil do 
    begin 
     ProcessTreeItem(tn, cNode); 
     tn := tn.getNextSibling; 
    end; 
    end; (*ProcessTreeItem*) 
begin 
    XMLDoc := TXMLDocument.Create(nil); 
    XMLDoc.Active := True; 
    iNode := XMLDoc.AddChild('tree2xml'); 
    iNode.Attributes['app'] := ParamStr(0); 

    tn := tree.TopItem; 
    while tn <> nil do 
    begin 
    ProcessTreeItem (tn, iNode); 

    tn := tn.getNextSibling; 
    end; 

    XMLDoc.SaveToFile(ChangeFileExt(ParamStr(0),'.XML')); 
    XMLDoc := nil; 
end; (* Form2XML *) 

或修改實施:

procedure ProcessNode(Node : IXMLNode; tn : TTreeNode); 
var 
    cNode : IXMLNode; 
begin 
    if Node = nil then Exit; 
    with Node do 
    begin 
    tn := tree.Items.AddChild(tn, Attributes['text']); 
    tn.ImageIndex := Integer(Attributes['imageIndex']); 
    tn.StateIndex := Integer(Attributes['stateIndex']); 
    end; 

    cNode := Node.ChildNodes.First; 
    while cNode <> nil do 
    begin 
    ProcessNode(cNode, tn); 
    cNode := cNode.NextSibling; 
    end; 
end; (*ProcessNode*) 

procedure ProcessTreeItem(tn : TTreeNode; iNode : IXMLNode); 
var 
    cNode : IXMLNode; 
begin 
    if (tn = nil) then Exit; 
    cNode := iNode.AddChild('item'); 
    cNode.Attributes['text'] := tn.Text; 
    cNode.Attributes['imageIndex'] := tn.ImageIndex; 
    cNode.Attributes['stateIndex'] := tn.StateIndex; 
    cNode.Attributes['selectedIndex'] := tn.SelectedIndex; 

    //child nodes 
    tn := tn.getFirstChild; 
    while tn <> nil do 
    begin 
    ProcessTreeItem(tn, cNode); 
    tn := tn.getNextSibling; 
    end; 
end; (*ProcessTreeItem*) 

procedure XML2Form(tree : TJvPageListTreeView; XMLDoc : TXMLDocument); 
var 
    iNode : IXMLNode; 
begin 
    tree.Items.Clear; 
    XMLDoc.FileName := ChangeFileExt(ParamStr(0),'.XML'); 
    XMLDoc.Active := True; 

    iNode := XMLDoc.DocumentElement.ChildNodes.First; 

    while iNode <> nil do 
    begin 
    ProcessNode(iNode,nil); 
    iNode := iNode.NextSibling; 
    end; 
    XMLDoc.Active := False; 
end; 

procedure Form2XML(tree: TJVPageListTreeView); 
var 
    tn : TTreeNode; 
    XMLDoc : TXMLDocument; 
    iNode : IXMLNode; 
begin 
    XMLDoc := TXMLDocument.Create(nil); 
    XMLDoc.Active := True; 
    iNode := XMLDoc.AddChild('tree2xml'); 
    iNode.Attributes['app'] := ParamStr(0); 

    tn := tree.TopItem; 
    while tn <> nil do 
    begin 
    ProcessTreeItem (tn, iNode); 

    tn := tn.getNextSibling; 
    end; 

    XMLDoc.SaveToFile(ChangeFileExt(ParamStr(0),'.XML')); 
    XMLDoc := nil; 
end; (* Form2XML *) 
+4

這個問題歸結爲「爲什麼帕斯卡像帕斯卡,而不是像C?」。 :-)然而,我並沒有投票結束,因爲這是一個合法的新用戶問題。 –

+2

爲什麼要在程序中聲明變量而不是使其成爲全局變量?我使用這個推理來有時在我的程序中使用程序,而沒有另一個全局程序。 –

回答

16

像這樣的嵌套過程在這個與XML相關的代碼中有意義。爲了處理所有節點,需要ProcessNode遞歸調用。必須注意的是,有時候,內部函數需要比少數參數訪問更多的數據。

潛在的實現可能是:

  • 使用 「扁平化」 程序,在您的實現;
  • 使用「嵌套」過程,如在原始實施中;
  • 創建一個專用class(或record +方法),它將保持私有implementation部分的單位。

當然,第三個選項聽起來更容易維護。它將允許清楚地分離該過程,允許在其方法中使用局部變量。使用record(或object舊版本的Delphi)將允許處理對象主要程序的堆棧上分配的,所以你不需要寫Obj := TInterType.Create; try .. finally Obj.Free。但是,如果您使用的是object,請注意Delphi has compilation issue的一些新版本 - 您應該更好地使用record的方法。

的「扁平化」過程的風格是恕我直言並不比「嵌套」的過程更好,更糟糕的,因爲它需要額外的參數添加到內部電話或者使用一些全局變量。順便說一句,每次調用都有很多變量會增加堆棧空間並降低速度。

「嵌套」風格實際上是面向對象的。當調用內部函數時,編譯器將寄存器中的調用者堆棧庫傳遞給嵌套函數(就像對象的附加self參數一樣)。所以內部函數可以訪問所有的調用者堆棧變量,就像它們是在一個私有對象中聲明的一樣(第三個解決方案)。

德爾福IDE和調試器內部嵌套處理程序相當不錯。恕我直言,它可能是有意義的一小段代碼(即,可以在相同的屏幕高度讀取的東西)。然後,當你需要更多的流程時,帶有方法和顯式變量的專用record/object將更易於維護。但「平」選項恕我直言,不被編碼。

我剛剛written a blog article about these implementation patterns,它會呈現出快速排序實施,這將使用盡可能少的堆棧空間儘可能的一些源代碼,並且將避免一個過程內部嵌套過程的調用,並使用專用私人object

在所有情況下,不要害怕創建一些內部對象/類來實現您的算法。最新版本的Delphi甚至允許私人類型在class定義 - 但有時,我覺得使內部對象完全私有的implementation部分的單位,即非單位的私人成員的部分顯示非常舒服。

類不僅適用於在單元之外發布您的過程:OOP也適用於實現模式。您的代碼將更易於維護,並且在大多數情況下,self參數將用於一次引用所有關聯數據,因此您的代碼可能更快更輕!

4

還有一個問題,您的修訂版:它引用tree,這是主要的方法的參數。這是嵌套過程可以完成的一件事情:它們可以訪問迄今爲止已聲明的外部範圍的任何變量。儘管如此,許多開發人員發現嵌套過程是凌亂的編碼風格,並且更喜歡避免它;他們通常會像你一樣重寫它,但是添加tree作爲ProcessNode的另一個參數。

+0

+1我沒有發現嵌套的程序凌亂,除非它們很長,一個小的遞歸過程值得一萬億bux! (: – ComputerSaysNo

+3

有些人喜歡隱藏那些不應該在他們的設計使用範圍之外被調用的東西。這種「隱藏」不僅存在於對象中(帶有私有/受保護的),它也是嵌套過程的原因,也是Wirth將該功能放入語言中的原因(結構編程)。通過製作一個只在實現部分中使用和聲明的非本地函數,可以完成大部分目的,然後,您所失去的就是共享本地作用域的能力。 –

11

這樣的內部程序編碼是一個風格問題。有人可能會說這是「乾淨」 ......在封裝內一個啄所有相關的數據和程序的相同的意義,因爲人們聽到關於「面向對象編程」 ...但它也有缺點:比較難最初編碼正確,難以測試,對於許多程序員來說難以理解(因此可能難以維護)。

定義一個內部過程使得未來的程序員不會意外地調用內部過程,並期望它做一些合理的事情。內部程序甚至沒有被界定 - 這在外部/全球層面上是不能被稱爲的。

限定了內部程序也意味着更少的外部/全局命名空間名稱衝突的機會,因爲內部程序不利於其他類似的命名空間。 (這是一個很好的例子:所有命名爲「ProcessNode(...)」的可能有多少個不同的東西?)

並且如上所述,在大多數語言中,內部例程具有「特殊」訪問權,本地數據類型和變量。

1

嵌套過程/函數已經在德爾福長期OOP HAST被添加到它之前已經可用。這一切都發生在25年前。回到那個時代,函數內部的本地函數幫助保持全局範圍更清晰和相關的代碼更接近。 Borland/Inprise/Embarcadero從未放棄該功能,當然,因爲否則它們會造成巨大的不兼容性。 因此,如果它對您有意義,那麼使用它,否則就讓它成爲。

+0

請嘗試格式化答案,使其變得更具可讀性。 – Naddy