我在网上找到了这段代码,其中有一个过程内有一个过程。 我不明白为什么作者会选择这样写。 我注意到的是正在执行的递归函数。
为什么他不像我见过的大多数代码那样将程序分开。
他的实现:
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 *)
最佳答案
像这样的嵌套过程在这个 XML 相关代码中确实有意义。要处理所有节点,需要对 ProcessNode
进行递归调用。您必须注意,有时,内部函数需要访问的数据远多于几个参数。
潜在的实现可能是:
- 在您的实现中使用“扁平”程序;
- 使用“嵌套”过程,如原始实现中一样;
- 创建一个专用的
类
(或记录
+方法),该类对于实现
部分保持私有(private)单位。
当然,第三个选项听起来更易于维护。它将允许过程的清晰分离,并且允许使用其方法的本地变量。使用记录
(或旧版本Delphi的对象
)将允许在主过程的堆栈上分配处理对象,因此您不需要写Obj := TInterType.Create;尝试..最后Obj.Free
。但如果您使用对象
,请注意一些新版本的Delphi has compilation issue - 你应该更好地将record
与方法一起使用。
恕我直言,“平面”过程风格并不比“嵌套”过程更好,甚至更糟,因为它需要向内部调用添加额外的参数,或者使用一些全局变量。顺便说一下,每次调用都有很多变量会增加堆栈空间,并降低速度。
“嵌套”风格实际上是面向OOP的。当调用内部函数时,编译器将寄存器中的调用者堆栈基址传递给嵌套函数(就像对象的附加 self
参数一样)。因此内部函数能够访问所有调用者堆栈变量,就像它们在私有(private)对象中声明一样(第三种解决方案)。
Delphi IDE 和内部调试器可以很好地处理嵌套过程。恕我直言,这对于一些小代码段(即可以在相同屏幕高度上读取的代码)来说可能是有意义的。然后,当您需要更多流程时,带有方法和显式变量的专用记录/对象
将更易于维护。但恕我直言,“扁平”选项不需要编码。
我刚刚 written a blog article about these implementation patterns ,它将呈现 QuickSort 实现的一些源代码,它将使用尽可能少的堆栈空间,并将避免调用过程内的嵌套过程,并使用专用的私有(private) 相反,对象
。
在所有情况下,不要害怕创建一些内部对象/类来实现您的算法。 Delphi 的最新版本甚至允许在 class
定义中使用私有(private)类型 - 但有时,我觉得让内部对象完全私有(private)于单元的 implementation
部分会更舒服,即甚至不作为单元的接口(interface)
部分的私有(private)成员出现。
类不仅用于在单元外部发布流程:OOP 还适用于实现模式。您的代码将更易于维护,并且在大多数情况下,self
参数将用于一次性引用所有关联数据,因此您的代码也可能更快、更轻!
关于delphi - 程序内的程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10670840/