Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Naming and calling anonymous arrays

Status
Not open for further replies.

noogrub

MIS
Feb 3, 2008
2
US
Hi,
I am writing a genetic algorithm which allows users to seed the program run with some number of "critters". I want to use this number to generate a string (the "gene") for each "critter", then associate the name of each critter and its gene in a hash. No problem.

However, when I then wish to create an array to hold the round-by-round history of each "critter's" behavior, I am stuck. I -think- I want to create the array on the fly and then store it in a hash using the critter's name as a keyword. I seem to be able to do this, so long as I turn OFF "use strict;" (see code below) I modeled this "behavior" by using random $x to represent some sub-routine output.

I hope to be able to end up with a
hash {
Critter1 -> ARRAY_Critter1
Critter2 -> ARRAY_Critter2
.... etc
}
where each ARRAY_CritterX has some series of values: (1,6,12,34,8,14...etc)

The question is two-part: 1. What am I doing wrong??? and 2. How can accomplish this using strict pragma?

Thanks in advance. John
Code:
#------------------------------------------------
my $i=5;           #User-selected number of critters
my $name; 
my %hash=();       #Hash of "Critter->ARRAY of Behaviors"

print "KEY \tVALUE\n";

while ($i>0) {
        my $x = int(rand(20));     #Simulate Behavior results
        $name="Critter"."$i";      #Generate each Critter
        my $name_ref= "$name";
        my $array_name="$name"."history";
        push(@$array_name,$x);
        print "$array_name -> @$array_name[0]\n";  #Print first value in array to verify this works
        $hash{$name_ref}=$array_name->[0];
        $i--;
}

#-----------------------------------------------
#Now print out the hash and look at it
while (my ($key, $value) = each (%hash)) {
        print "$key \t$value\n";
}
 
John,

You're doing some weird stuff there that probably isn't necessary, it makes it a bit confusing so I'm not 100% sure that this is what you want.

I think you're looking for nested hashes and array references instead of actual hashes and arrays.

Does this design fit the bill?
Code:
my %critters = ();

while ($i>0) {
  my $x = int(rand(20));

  [COLOR=grey]# If this critter doesn't exist yet, initialize it[/color]
  unless ($critters{"Critter$i"}) {
    [COLOR=grey]# Each critter can have a set of properties, I added age as an example[/color]
    $critters{$i} = {
      history => [], [COLOR=grey]# An array reference stores history[/color]
      age => 0
    };
  }
  
  [COLOR=grey]# Use @{} to use the history value for this creature as an array[/color]
  push(@{$critters{"Critter$i"}->{history}}, $x);

  [COLOR=grey]# So you can put array refs, hash refs, or scalars like age in your critter[/color]
  $critters{"Critter$i"}->{age}++;

  $i--;
}

[COLOR=grey]# Print info about each critter[/color]
foreach my $key (keys %critters}) {
  print "$key (age: $critters{$key}->{age})\n";
  
  [COLOR=gray]# Print each step that the critter took[/color]
  foreach my $step (@{$critters{$key}->{history}}) {
    print "\t$step\n";
  }
}

You can do some research on hash refs and array refs to learn more about creating references with {} [] and \ as well as the dereferencing operators -> @{} %{} and other caveats.
 
Oops I missed one in the unless block... [tt]$critters{$i}[/tt] should be [tt]$critters{"Critter$i"}[/tt] like the rest of them.

Also, did you know that perl interpolates your strings so that your [tt]$name = "Critter"."$i";[/tt] doesn't really need the concatenation character ([tt]$name = "Critter$i";[/tt]). You just have to make sure perl knows what variable you're trying to use. Later on you have [tt]"$name"."history"[/tt] which is fine, but could also be written [tt]"${name}history"[/tt]. The {} shows perl that you want the variable [tt]$name[/tt] and not [tt]$namehistory[/tt]. You only need the curly brackets when your variable is immediately followed by letters, numbers, or an underscore (since perl variables can't have other symbols in their names). Just a tidbit to save some time in the future, especially if you're building long strings with a lot of variables!
 
For your test script a hash of arrays is not necessary, but if the purpose is to learn how to make a hash of arrays on the fly your effort is over-engineered, which is common when learning how to do something.

Code:
[url=http://perldoc.perl.org/functions/use.html][black][b]use[/b][/black][/url] [green]warnings[/green][red];[/red]
[black][b]use[/b][/black] [green]strict[/green][red];[/red]
[gray][i]#------------------------------------------------[/i][/gray]
[url=http://perldoc.perl.org/functions/my.html][black][b]my[/b][/black][/url] [blue]$i[/blue]=[fuchsia]5[/fuchsia][red];[/red]           [gray][i]#User-selected number of critters[/i][/gray]
[black][b]my[/b][/black] [blue]$name[/blue][red];[/red]
[black][b]my[/b][/black] [blue]%hash[/blue]=[red]([/red][red])[/red][red];[/red]       [gray][i]#Hash of "Critter->ARRAY of Behaviors"[/i][/gray]

[url=http://perldoc.perl.org/functions/print.html][black][b]print[/b][/black][/url] [red]"[/red][purple]KEY [purple][b]\t[/b][/purple]VALUE[purple][b]\n[/b][/purple][/purple][red]"[/red][red];[/red]

[olive][b]while[/b][/olive] [red]([/red][blue]$i[/blue]>[fuchsia]0[/fuchsia][red])[/red] [red]{[/red]
        [url=http://perldoc.perl.org/functions/push.html][black][b]push[/b][/black][/url][red]([/red][blue]@[/blue][red]{[/red][blue]$hash[/blue][red]{[/red][red]"[/red][purple]Critter[blue]$i[/blue][/purple][red]"[/red][red]}[/red][red]}[/red],[url=http://perldoc.perl.org/functions/int.html][black][b]int[/b][/black][/url][red]([/red][url=http://perldoc.perl.org/functions/rand.html][black][b]rand[/b][/black][/url][red]([/red][fuchsia]20[/fuchsia][red])[/red][red])[/red][red])[/red][red];[/red]
        [blue]$i[/blue]--[red];[/red]
[red]}[/red]

[gray][i]#-----------------------------------------------[/i][/gray]
[gray][i]#Now print out the hash and look at it[/i][/gray]
[olive][b]while[/b][/olive] [red]([/red][black][b]my[/b][/black] [red]([/red][blue]$key[/blue], [blue]$value[/blue][red])[/red] = [url=http://perldoc.perl.org/functions/each.html][black][b]each[/b][/black][/url] [red]([/red][blue]%hash[/blue][red])[/red][red])[/red] [red]{[/red]
        [black][b]print[/b][/black] [red]"[/red][purple][blue]$key[/blue] [purple][b]\t[/b][/purple][blue]@[/blue]{[blue]$value[/blue]}[purple][b]\n[/b][/purple][/purple][red]"[/red][red];[/red]
[red]}[/red]
[tt]------------------------------------------------------------
Pragmas (perl 5.8.8) used :
[ul]
[li]strict - Perl pragma to restrict unsafe constructs[/li]
[li]warnings - Perl pragma to control optional warnings[/li]
[/ul]
[/tt]

This appears to be where your script bombs with strict:

my $array_name="$name"."history";
push(@$array_name,$x);

What you have is a soft reference in the second line, you can't use soft references with strict on. But you can see in my code that all of that was not necessary anyway.

------------------------------------------
- Kevin, perl coder unexceptional! [wiggle]
 
Thank you both very much! Both answers are helpful and esp thanks for taking the time to figure out my spaghetti!

"There's more than one way to do it" is no joke... my corollary is "There's at least one way to overdo it".

Cheers and thanks again! 8^)
John
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top