Re: A good data structure to store INI files.



On Tue, 10 Feb 2009 21:34:36 +0100, Marc Lucksch <perl@xxxxxxxxx> wrote:

sln@xxxxxxxxxxxxxxx schrieb:
On Tue, 10 Feb 2009 19:53:51 GMT, sln@xxxxxxxxxxxxxxx wrote:

On Tue, 10 Feb 2009 19:42:57 GMT, sln@xxxxxxxxxxxxxxx wrote:
The other alternative is where each section and key a hash key
that is an array reference consisting of the order obtained, type and
a value array reference (undef on sections). Sort the orer and print
out the section/key/value.

-sln

%ini = {
section=>[[order,name,key_index],..],
^^^^
order is not needed in this case
[snip]

Well it is a good idea, but explaining that to potential users might be
difficult.
The other way includes a lot of objects, saving it as an array and an
hash for lookup.

while(my ($section,$data)=$ini->each()) {
while (my ($key,$value)=$data->each()) {
for my $entr (@$value) {
...
}
}
}
$ini->section("Ship")->key("fuse")->[0]; #First ship, first fuse

$ini->section("Ship",7)->key("fuse",1)->[0];

foreach my $section (@{$ini->sections("Ship"}) {
$section->setkey("nickname",$section->key("nickname")."_v2");
}

This would work, but it is hard to explain to any users and will scare
them (like POE does to me). Who wants to read 3 pages of POD just to
change a single value. :(

foreach my $section (@{$ini->{Ship}}) {
$section->{nickname}.="_v2";
}

This is shorter but I can't find any way to do it.

Well, I'm going to bed, gotta go to the dentist tomorrow, where there
will be joy and nice things will happen to me. Happy thoughts, Happy
thoughts. No pain. Happy thoughts

Marc "Maluku" Lucksch

Excuse the below verbosity. There is one other way, a single linked list.
Clean and simple. Below is a generalized double linked list schema based
on a Node, with lots of maybe 'bloated' utility functions you could trim
down.

In your case the Node would contain a single point of Section/Key/Value
but within a linked list, so order is clear and duplictes are no problem.
Access is simple.

See below, its easy to add or insert a node. Its easy to traverse the list.
And the good part is you can rename the functions for the user AND you can
even generate an ini file after the list is edited, ie: insert/remove, clear,
add head tail, etc ...

The Node structure for your ini usage would be of this form. You will have to
adjust the exapmles before "Node Functions" in examples, for data hash change,
not sure, check it out.

I highly recommend this method.
-sln

*******************************************************

sub new_node # modified (correct below)
{
my $node = {
# Node Header
prev => undef,
next => undef,
# Node Data
data => { section => '',
key => '',
value => ''
}
};
return $node;
}

So, traversal is something like this:
# Traverse the list from the Head, print data
#-
print "\nTraverse list from Head -\n";
$curnode = $ListHead;
while (defined $curnode)
{
if (defined $curnode->{data}->{val}) {

### Use Data Dumper on $curnode->{data}->{val}
}
$curnode = $curnode->{next};
}
print "sleeping 2 seconds\n";
sleep(2);



----------------------------
----------------------------
## nodes.pl
## -sln

use strict;
use warnings;

### Exercise List Creation and Destruction,
### the bare minimum.
### ---------------------------------------


# Create a new List with 1,000,000 nodes
#-
my $ListHead = CreateList();
my $ListTail = $ListHead;
my $curnode = $ListTail;
$curnode->{data}->{val} = 0;

print "\nAdding 1,000,000 nodes\n";
for (my $i=1; $i<1000000; $i++)
{
$curnode = CreateTail( $curnode );
$curnode->{data}->{val} = $i;
$ListTail = $curnode;
}
print "Done, sleeping 5 seconds\n";
sleep(5);

# Destroy it
#-
print "\nDestroying List\n";
DestroyList( $ListHead );
print "Done\n";


# Create a different List with 1,500,000 nodes
#-
$ListHead = CreateList();
$ListTail = $ListHead;
$curnode = $ListTail;
$curnode->{data}->{val} = 0;

print "\nAdding 1,500,000 nodes\n";
for (my $i=1; $i<1500000; $i++)
{
$curnode = CreateTail( $curnode );
$curnode->{data}->{val} = $i;
$ListTail = $curnode;
}
print "Done, sleeping 5 seconds\n";
sleep(5);

# Destroy it
#-
print "\nDestroying List\n";
DestroyList( $ListHead );
print "Done\n";


# Traverse the list from the Head, print data
#-
print "\nTraverse list from Head -\n";
$curnode = $ListHead;
while (defined $curnode)
{
if (defined $curnode->{data}->{val}) {
print "data is: val = $curnode->{data}->{val}\n";
}
$curnode = $curnode->{next};
}
print "sleeping 2 seconds\n";
sleep(2);

# Traverse the list from the Tail, print data
#-
print "\nTraverse list from Tail -\n";
$curnode = $ListTail;
while (defined $curnode)
{
if (defined $curnode->{data}->{val}) {
print "data is: val = $curnode->{data}->{val}\n";
}
$curnode = $curnode->{prev};
}

## =================================
## Node Functions
## =================================

# Make a new node
# Parameters- none
# Return - ref to a new node
# -
sub new_node
{
my $node = {
# Node Header
prev => undef,
next => undef,
# Node Data
data => {val => undef}
};
return $node;
}

# Create a new List
# Parameters - None
# Return - ref to a created List (ListHead)
# -
sub CreateList
{
return new_node();
}

# Destroy the List given any Node
# Parameters - $Node = any node in the List
# Return - None
# Notes - Destructive on all List node's
# -
sub DestroyList
{
my $Node = shift;
if (defined $Node)
{
TruncateListAfter( $Node );
TruncateListBefore( $Node );
undef (%{$Node});
}
}

# Create a new List Head from the given Node
# Parameters - $Node = find the head from this node, prepend new created head,
# creates new List if undef
# Return - ref to a created node (ListHead)
# -
sub CreateHead
{
my $Node = shift;
if (defined $Node)
{
while (defined $Node->{prev}) {
$Node = $Node->{prev};
}
}
return CreateHeadAtNode( $Node );
}

# Create a new List Tail from the given Node
# Parameters - $Node = find the tail from this node, append new created tail,
# creates new List if undef
# Return - ref to a created node (ListTail)
# Notes - Destructive on truncated node's
# -
sub CreateTail
{
my $Node = shift;
if (defined $Node)
{
while (defined $Node->{next}) {
$Node = $Node->{next};
}
}
return CreateTailAtNode( $Node );
}

# Create a new Head before Node, removes leading node's
# Parameters - $Node = node to prepend new head,
# creates new List if undef
# Return - ref to a created node (ListHead)
# -
sub CreateHeadAtNode
{
my $Node = shift;
my $ret = undef;
if (defined $Node)
{
my $newnode = new_node();
if (defined $Node->{prev}) {
TruncateListBefore( $Node );
}
$newnode->{next} = $Node;
$Node->{prev} = $newnode;
$ret = $newnode;
} else {
$ret = new_node();
}
return $ret;
}

# Create a new Tail after Node, removes trailing node's
# Parameters - $Node = node to append new tail,
# creates new List if undef
# Return - ref to a created node (ListTail)
# Notes - Destructive on truncated node's
# -
sub CreateTailAtNode
{
my $Node = shift;
my $ret = undef;
if (defined $Node)
{
my $newnode = new_node();
if (defined $Node->{next}) {
TruncateListAfter( $Node );
}
$newnode->{prev} = $Node;
$Node->{next} = $newnode;
$ret = $newnode;
} else {
$ret = new_node();
}
return $ret;
}

# Add the passed in Node to the List as its new Head
# Parameters - $Node = find the head from this node, prepend new head
# - $NewNode = the new node
# Return - ref to the added node (ListHead)
# -
sub AddHead
{
my ($Node, $NewNode) = @_;
my $ret = undef;
if (defined $Node && defined $NewNode)
{
while (defined $Node->{prev}) {
$Node = $Node->{prev};
}
$ret = AddHeadAtNode( $Node, $NewNode );
}
return $ret;
}

# Add the passed in Node to the List as its new Tail
# Parameters - $Node = find the tail from this node, append new tail
# - $NewNode = the new node
# Return - ref to the added node (ListTail)
# -
sub AddTail
{
my ($Node, $NewNode) = @_;
my $ret = undef;
if (defined $Node && defined $NewNode)
{
while (defined $Node->{next}) {
$Node = $Node->{next};
}
$ret = AddTailAtNode( $Node, $NewNode );
}
return $ret;
}

# Add a new Head before Node, removes leading nodes
# Parameters - $Node = node to prepend new head
# - $NewNode = the new node
# Return - ref to the added node (ListHead)
# Notes - Destructive on truncated node's
# -
sub AddHeadAtNode
{
my ($Node, $NewNode) = @_;
my $ret = undef;
if (defined $Node && defined $NewNode)
{
my $newnode = $NewNode;
if (defined $Node->{prev}) {
TruncateListBefore( $Node );
}
$newnode->{next} = $Node;
$Node->{prev} = $newnode;
$ret = $newnode;
}
return $ret;
}

# Add a new Tail after Node, removes trailing nodes
# Parameters - $Node = node to append new tail
# - $NewNode = the new node
# Return - ref to the added node (ListTail)
# Notes - Destructive on truncated node's
# -
sub AddTailAtNode
{
my ($Node, $NewNode) = @_;
my $ret = undef;
if (defined $Node && defined $NewNode)
{
my $newnode = $NewNode;
if (defined $Node->{next}) {
TruncateListAfter( $Node );
}
$newnode->{prev} = $Node;
$Node->{next} = $newnode;
$ret = $newnode;
}
return $ret;
}

# Truncate a List after a Node (Node is intact)
# Parameters - $Node = node to disconnect from
# Return - None
# Notes - Destructive on truncated node's
# -
sub TruncateListAfter
{
my $Node = shift;
if (defined $Node)
{
my $curnode = $Node->{next};
while (defined $curnode)
{
my $nextnode = $curnode->{next};
undef (%{$curnode});
$curnode = $nextnode;
}
$Node->{next} = undef;
}
}

# Truncate a List before a Node (Node is intact)
# Parameters - $Node = node to disconnect from
# Return - None
# Notes - Destructive on truncated node's
# -
sub TruncateListBefore
{
my $Node = shift;
if (defined $Node)
{
my $curnode = $Node->{prev};
while (defined $curnode)
{
my $prevnode = $curnode->{prev};
undef (%{$curnode});
$curnode = $prevnode;
}
$Node->{prev} = undef;
}
}

# Create a new node after a node
# Parameters - $Node = node to append to
# Return - ref to a created node
# -
sub CreateNextNode
{
my $Node = shift;
return InsertNextNode( $Node, new_node() );
}

# Create a new node before a node
# Parameters - $Node = node to prepend to
# Return - ref to a created node
# -
sub CreatePrevNode
{
my $Node = shift;
return InsertPrevNode( $Node, new_node() );
}


# Insert a passed in node after a node
# Parameters - $Node = node to append to
# - $NewNode = the new node
# Return - ref to the inserted node
# -
sub InsertNextNode
{
my ($Node, $NewNode) = @_;
my $ret = undef;
if (defined $Node && defined $NewNode)
{
my $newnode = $NewNode;
my $nextnode = $Node->{next};

if (defined $nextnode) {
$nextnode->{prev} = $newnode;
$newnode->{next} = $nextnode;
}
$newnode->{prev} = $Node;
$Node->{next} = $newnode;
$ret = $newnode;
}
return $ret;
}

# Insert a passed in node before a node
# Parameters - $Node = node to prepend to
# - $NewNode = the new node
# Return - ref to the inserted node
# -
sub InsertPrevNode
{
my ($Node, $NewNode) = @_;
my $ret = undef;
if (defined $Node && defined $NewNode)
{
my $newnode = $NewNode;
my $prevnode = $Node->{prev};

if (defined $prevnode) {
$prevnode->{next} = $newnode;
$newnode->{prev} = $prevnode;
}
$newnode->{next} = $Node;
$Node->{prev} = $newnode;
$ret = $newnode;
}
return $ret;
}

# Remove node from list
# Parameters - $Node = node ref
# Return - ref to next node
# Notes - Destructive on Node
# -
sub RemoveNodeRetNext
{
my $Node = shift;
my $ret = undef;
if (defined $Node)
{
my $nextnode = $Node->{next};
my $prevnode = $Node->{prev};
undef (%{$Node});

if (defined $nextnode && defined $prevnode) {
$nextnode->{prev} = $prevnode;
$prevnode->{next} = $nextnode;
$ret = $nextnode;
} elsif (defined $nextnode) {
$nextnode->{prev} = undef;
$ret = $nextnode;
} elsif (defined $prevnode) {
$prevnode->{next} = undef;
}
}
return $ret;
}

# Remove node from list
# Parameters - $Node = node ref
# Return - ref to prev node
# Notes - Destructive on Node
# -
sub RemoveNodeRetPrev
{
my $Node = shift;
my $ret = undef;
if (defined $Node)
{
my $nextnode = $Node->{next};
my $prevnode = $Node->{prev};
undef (%{$Node});

if (defined $nextnode && defined $prevnode) {
$nextnode->{prev} = $prevnode;
$prevnode->{next} = $nextnode;
$ret = $prevnode;
} elsif (defined $prevnode) {
$prevnode->{next} = undef;
$ret = $prevnode;
} elsif (defined $nextnode) {
$nextnode->{prev} = undef;
}
}
return $ret;
}
__END__




.



Relevant Pages