I have VirtualStringTree (VST) with nodes that have the next data structure :
Type
PMyNodeData = ^TMyNodeData;
TMyNodeData = record
Cost:UnicodeString;
end;
The Child nodes which they don't have any children has Cost value but any parent nodes has cost value equaled to 0 and am trying to write procedure to make any parent node cost value equaled to sum of all its children cost .
I have tried the next :
procedure CalculateSum(Node:PVirtualNode);
var
Data: PMyNodeData;
ChildNode: PVirtualNode;
ChildData: PMyNodeData;
CostSum: Extended;
begin
Data := VST.GetNodeData(Node);
CostSum := 0.0;
ChildNode := VST.GetFirstChild(Node);
while Assigned(ChildNode) do
begin
ChildData := VST.GetNodeData(ChildNode);
CostSum := CostSum StrToFloat(ChildData.Cost);
CalculateSum(ChildNode);
ChildNode := VST.GetNextSibling(ChildNode);
end;
if VST.HasChildren[Node] and (StrToFloat(Data.Cost) = 0) then
Data.Cost := FloatToStr(CostSum);
end;
The use :
CalculateSum(vst.RootNode);
But I get Access violation & the sum is not correct .. any suggestions.
CodePudding user response:
You are accessing a child's Cost
before it has been calculated. You need to call CalculateSum(ChildNode)
before adding its Cost
to CostSum
, rather than after as you are currently doing.
Also, you did not specify whether PMyDataNode
is ever nil
, or whether TMyNodeData.Cost
contains a blank string for nodes whose value is 0. StrToFloat()
will raise an exception if you give it a blank string. If that is a possibility, use StrToFloatDef()
or TryStrToFloat()
instead.
Try something more like this:
procedure CalculateSum(Node: PVirtualNode);
var
Data, ChildData: PMyNodeData;
ChildNode: PVirtualNode;
Cost, CostSum: Extended;
begin
if not VST.HasChildren[Node] then Exit;
Data := VST.GetNodeData(Node);
if Data = nil then Exit;
CostSum := 0.0;
ChildNode := VST.GetFirstChild(Node);
while Assigned(ChildNode) do
begin
ChildData := VST.GetNodeData(ChildNode);
if ChildData <> nil then
begin
CalculateSum(ChildNode);
if TryStrToFloat(ChildData.Cost, Cost) then
CostSum := CostSum Cost;
// or:
// CostSum := CostSum StrToFloatDef(ChildData.Cost, 0.0);
end;
ChildNode := VST.GetNextSibling(ChildNode);
end;
Data.Cost := FloatToStr(CostSum);
end;
That being said, I would suggest storing TMyNodeData.Cost
as a Double
/Extended
instead of a UnicodeString
. All of those conversions are just unnecessary overhead.
Try this instead:
Type
PMyNodeData = ^TMyNodeData;
TMyNodeData = record
Cost: Extended;
end;
...
procedure CalculateSum(Node: PVirtualNode);
var
Data, ChildData: PMyNodeData;
ChildNode: PVirtualNode;
CostSum: Extended;
begin
if not VST.HasChildren[Node] then Exit;
Data := VST.GetNodeData(Node);
if Data = nil then Exit;
CostSum := 0.0;
ChildNode := VST.GetFirstChild(Node);
while Assigned(ChildNode) do
begin
ChildData := VST.GetNodeData(ChildNode);
if ChildData <> nil then
begin
CalculateSum(ChildNode);
CostSum := CostSum ChildData.Cost;
end;
ChildNode := VST.GetNextSibling(ChildNode);
end;
Data.Cost := CostSum;
end;
CodePudding user response:
I have solved the issue as the next :
procedure CalculateSum(Node: PVirtualNode);
var
Data: PMyNodeData;
ChildNode: PVirtualNode;
ChildData: PMyNodeData;
CostSum: Extended;
begin
Data := VST.GetNodeData(Node);
if data<>nil then
CostSum := Data.Cost else Costsum:=0.0;
ChildNode := VST.GetFirstChild(Node);
while Assigned(ChildNode) do
begin
ChildData := VST.GetNodeData(ChildNode);
if ChildData <> nil then
begin
CalculateSum(ChildNode);
CostSum := CostSum ChildData.Cost;
end;
ChildNode := VST.GetNextSibling(ChildNode);
end;
if (VST.HasChildren[Node]) and (Data<>nil) then
Data.Cost := CostSum;
end;