I am trying to make a state machine in Perl. To do this I have an array indexed by statenames. I can put subs into this array. Like this:
use constant {
stInit => 0,
stHeader => 1,
stSalesHeader => 2,
stCatagory => 3,
stData => 4,
stTotal => 5,
stError => 6,
};
my $state = stInit;
my #actions;
$actions[stInit] = [sub{logState("Initial state entered",2) }];
$actions[stHeader] = [sub{logState("Header state entered",2) }];
$actions[stSalesHeader] = [sub{logState("Sales header state entered",2) }];
$actions[stCatagory] = [sub{logState("Category state entered",2) }];
$actions[stData] = [sub{logState("Data state entered",2) }];
$actions[stTotal] = [sub{logState("Total state entered",2) }];
But then I have no Idea how to call the subroutine. I have tried this
$actions[$state]
But that appears not to work. Is this possible or am I completely off?
You really should add
use strict;
use warnings;
to the start of your code, which will find many simple mistakes for you. In this case your code is fine, and you can call your subroutines by using
$actions[$state][0]();
etc.
But there is no need to put the subroutines within square brackets, which just creates a one-element anonymous array and adds an additional level of indexing (hence the [0] in the above line of code. If you wrote code like this instead
$actions[stInit] = sub { logState("Initial state entered", 2) };
then you would be able to call the subroutines with
$actions[$state]();
On an a slightly different note, have you considered using FSA::Rules to write your state machine? It's fairly powerful, has optional GraphViz output and makes state machines rather easy to write.
You should do:
&{$actions[$state][0]}
but I'm not sure why you use an array... If you have just 1 function then
$actions[stData] = sub{ ... }
...
&{$actions[$state]}
will work. If you really want to execute many functions and use the array, then you can do:
map { &{$_} } #{$actions[$state]};
To call a subroutine from a reference:
&{$actions[$state]}();
However, based on your code, #actions does not contain subroutine references, but array references to the declaration of the subroutine.
first, declare the subs as you normally would and then build #actions:
$actions[0] = \&stInit;
$actions[1] = \&stHeader;
...and so on
Drop the extraneous anonymous array creation by removing the square brackets
$actions[stInit] = sub{logState("Initial state entered",2) };
You can then call the action with
$actions[stInit]();
If you have an action stored in a variable, e.g.
my $action = $actions[$actionID];
then you'll need a bit more syntax to make it actually do the call
$action->();
Then again, you could just use a hash instead of an array
my %actions = (
stInit => sub { logState("Initial state entered",2) },
stHeader => sub { logState("Header state entered",2) },
stSalesHeader => sub { logState("Sales header state entered",2) },
stCatagory => sub { logState("Category state entered",2) },
stData => sub { logState("Data state entered",2) },
);
which would save you from having to set up constants at the top. You could then call actions with
$actions{$state}();
Related
Using DBIx::Class, I found a solution to my issue, thankfully. But I'm sure there has to be a nicer way.
my $record = $schema->resultset("food")->create({name=>"bacon"});
How would I turn this record into a simple hashref instead of having to make this call right after.
my record = $schema->resultset("food")->search({name=>"bacon"})->hashref_array();
Ideally I want to be able to write a code snippet as simple as
{record=> $record}
instead of
{record => {name => $record->name, $record->food_id, ...}}
This would drive me insane with a table that has alot more columns.
I assume you're talking about DBIx::Class?
my $record = $schema->resultset("food")->create({name=>"bacon"});
my %record_columns = $record->get_columns;
# or, to get a HashRef directly
my $cols = { $record->get_columns };
# or, as you've asked for
my $foo = { record => { $record->get_columns } };
What you're looking for is included in DBIx::Class as DBIx::Class::ResultClass::HashRefInflator.
I need help regarding handling of Perl variables. Here I am getting input as a hash. I now need to send this hash variable to another subroutine. How can pass data as an argument to another subroutine? The code below shows how I am approaching this:
if ($csData->{'CUSTOMER_INVOICE_DETAILS'})
{
$c->log->debug("API Response:". Dumper $csData->{'CUSTOMER_INVOICE_DETAILS'});
my $Charges = [];
my #customerCharges = $csData->{'CUSTOMER_INVOICE_DETAILS'};
foreach(#customerCharges)
{
my ($customername,$customeramount) = split /:/;
my $charge_hash = ({
customername => $customername,
customeramount => $customeramount
});
push(#$Charges, $charge_hash);
}
my #ReturnCharges = $self->API->get_customer_charges($Charges, $Customer->customerid, $params->{'invoiceid'});
The other subroutine where this data is being received is as follows:
sub get_customer_charges
{
my $self = shift;
my ($charge, $CustomerId, $INID) = #_;
my $http_request = {
action => 'GetTariff',
customerid => $CustomerId,
csid => $INID,
};
my $markups = $self->APIRequest($http_request);
###Charge Level ID Inserting As 10
my #ChargeLevels;
my #BaseLevelID;
foreach my $ch (#$charge)
{
my ($customername,$customeramount) = split(':', $ch->{'customername'}, $ch->{'customername'});
my $chargelevel = join(':', $ch->{'customername'}, $ch->{'customeramount'}, '10');
push(#BaseLevelID, $chargelevel);
}
push(#ChargeLevels, #BaseLevelID);
return #ChargeLevels;
}
When I print to the server log for CUSTOMER_INVOICE_DETAILS variable I am getting the following values:
API Response:$VAR1 = {
'Product' => '34.04',
'basetax' => '2.38',
'vattax' => '4.36'
};
After sending data to second subroutine the data coming in server log for second subroutine variable is as following:
Charges in API:$VAR1 = 'HASH(0xb75d6d8)::10';
Can anyone help how could I send the hash data from one subroutine to another?
Given your comments and that your source is:
API Response:$VAR1 = {
'Product' => '34.04',
'basetax' => '2.38',
'vattax' => '4.36'
};
And you're looking for:
API Response:$VAR1 = { 34.04:2.38:4.36:10 };
(and somehow you're getting:
Charges in API:$VAR1 = 'HASH(0xb75d6d8)::10';
This suggests this may be as simple as using the values system call. values extracts an array of all the values in the hash. Something like this (guessing a bit on which part of your code needs it).
my #list_of_values = values ( %{$csData->{'CUSTOMER_INVOICE_DETAILS'}} );
You say you want to "convert" a hash to an array, but your issue seems more complex and subtle so simple conversion is not likely what will solve your problem. Something in your subroutine is returning a hash reference when the rest of your code does not expect it to do so. If the data-structure you are passing contains the correct information but not in the form you expect, then you can either change the code to produce it in the expected form (e.g. to return an ARRAY) or change your subroutine so that it is able to handle the data that it is passed correctly.
As for "converting a hash" per se, if your data structure doesn't contain complex nested references and all you want to do is "convert" your hash to an array or list, then you can simply assign the hash to an array. Perhaps I'm not understanding your question but if this kind of simple "flattening" is all you want then you could try:
my $customer_purchase = {
'Product' => '34.04',
'basetax' => '2.38',
'vattax' => '4.36'
};
my #flat_customer_purchase = %{ $customer_purchase };
say "#flat_customer_purchase" ;
Output:
basetax 2.38 Product 34.04 vattax 4.36
You can then supply the hash data as the "array" to the second subroutine. e.g. treat #flat_customer_purchase as a list:
use List::AllUtils ':all';
say join " ", pairkeys #flat_customer_purchase
# basetax Product vattax
say join " ", pairvalues #flat_customer_purchase
# 2.38 34.04 4.36
etc.
NB: this assumes that for some reason you must pass an array. The example of running the pairvalues routine simply replicates #Sobrique's suggestion to use values directly on the hash you are passing but in my answer this grabs the values pairs from the array instead of the hash.
My sense is that there is more to the question. If API Response is a more complicated hash/object or, if for some other reason this basic perl doesn't work, then you will have to supply more information. You need to find out where your unexpected hash reference is coming from before you can decide how to handle it. You might find this SO discussion helpful:
Are Perl subroutines call-by-reference or call-by-value?
I'm having some trouble figuring out how to create nested hashes in perl based on the text input.
i need something like this
my % hash = {
key1 => \%inner-hash,
key2 => \%inner-hash2
}
However my problem is I don't know apriori how many inner-hashes there would be. To that end I wrote the following piece of snippet to test if a str variable can be created in a loop and its reference stored in an array and later dereferenced.
{
if($line =~ m/^Limit\s+$mc_lim\s+$date_time_lim\s+$float_val\s+$mc\s+$middle_junk\s+$limit \s+$value/) {
my $str = $1 . ' ' . $2 . ' ' . $7;
push (#test_array_reference, \$str);
}
}
foreach (#test_array_reference) {
say $$_;
}
Perl dies with a not a scalar run-time error. I'm a bit lost here. Any help will be appreciated.
To answer your first (main?) question, you don't need to know how many hashes to create if you walk through the text and create them as you go. This example uses words of a string, delimited by spaces, as keys but you can use whatever input text for your purposes.
my $text = 'these are just a bunch of words';
my %hash;
my $hashRef = \%hash; # create reference to initial hash
foreach (split('\s', $text)){
$hashRef->{$_} = {}; # create anonymous hash for current word
$hashRef = $hashRef->{$_}; # walk through hash of hashes
}
You can also refer to any arbitrary inner hash and set the value by,
$hash{these}{are}{just}{a}{bunch}{of}{words} = 88;
$hash{these}{are}{just}{a}{bunch}{of}{things} = 42;
$hash{these}{things} = 33;
To visualize this, Data:Dumper may help,
print Dumper %hash;
Which generates,
$VAR1 = 'these';
$VAR2 = {
'things' => 33,
'are' => {
'just' => {
'a' => {
'bunch' => {
'of' => {
'things' => 42,
'words' => 88
}
}
}
}
}
};
my $hashref = { hash1 => { key => val,... },
hash2 => { key => val,..} };
also you may want to use the m//x modifier with your regex, its barely readable as it is.
Creating a hash of hashes is pretty simple:
my %outer_hash = {};
Not entirely necessary, but this basically means that each element of your hash is a reference to another hash.
Imagine an employee hash keyed by employee number:
$employee{$emp_num}{first} = "Bob";
$employee{$emp_num}{last} = "Smith";
$employee{$emp_num}{phones}{cell} = "212-555-1234";
$employee{$emp_num}{phones}{desk} = "3433";
The problem with this notation is that it gets rather hard to read after a while. Enter the arrow notation:
$employee{$emp_num}->{first} = "Bob";
$employee{$emp_num}->{last} = "Smith";
$employee{$emp_num}->{phones}->{cell} = "212-555-1234";
$employee{$emp_num}->{phones}->{desk} = "3433";
The big problem with complex structures like this is that you lose the use strict ability to find errors:
$employee{$emp_num}->{Phones}->{cell} = "212-555-1234";
Whoops! I used Phones instead of phones. When you start using this type of complex structure, you should use object oriented syntax. Fortunately, the perlobj tutorial is pretty easy to understand.
By the way, complex data structure handling and the ability to use object oriented Perl puts you into the big leagues. It's the first step into writing more powerful and complex Perl.
I have a recursive function that uses ref to walk down a data structure recursively. If ref returns an empty string, then a callback is called. My problem is that I need to store a hash-ref in my data structure that will be treated as a scalar by my function.
In essence, what I need is to do something like this;
my %hash = fillHash();
$hash{'abc'}{'def'} = \%hash;
I was wondering if there was some way of storing \%hash in such a way that it would be treated as a scalar and not a reference. possibly like/
$hash{'abc'}{'def'} = scalar \%hash;
I am simply looking a way to add a link to the parent node of my data structure, yet still being able to recursively walk it.
Thanks
You could use what I did to differentiate values from structure, I call them "indirect arrays" (in my case).
In your case it would look like this:
$hash{'abc'}{'def'} = scalar \\%hash;
ref( $hash{'abc'}{'def'} ) is 'REF', in case you're wondering. And you can then decide that you need to dereference it twice:
$hash_ref = ${ $hash{'abc'}{'def'} };
I think #MarkCanlas has the right solution in his suggestion to consider a different way to structure your data. Barring that, you could take a double reference.
my $scalar_ref = \%hash;
$other_hash{abc}{def} = \$scalar_ref;
Now, when you check the ref of that you'll get back "REF" and can do something different.
First of all, you should realize that all references are scalars by definition.
What you want is something that works as a reference everywhere but inside your walking function. That's not really achievable by any kind of magic reference. The walking function is where the logic is going to have to be added. You can make it keep track of things it has already seen:
my %h = (
foo => "bar",
inner => {
foo => 42
}
);
$h{inner}{parent} = \%h;
sub walk
{
my ($h, $cb, $path, $seen) = #_;
$path //= "";
$seen //= {};
$seen->{$h}=1;
while(my ($k, $v) = each %$h) {
if(ref($v)) {
walk($v, $cb, "$path/$k", $seen) if !$seen->{$v};
} else {
$cb->($k, $v, "$path/$k");
}
}
}
walk(\%h, sub {
my ($key, $value, $path) = #_;
print "walker found $key ($path) => $value\n";
});
Or you could just make it recognize certain keys like parent as special and skip them.
I have an array and I am making a hash instance from it.
For instance, if array is:
#folders=(temp,usr,bin);
then i want to fill in hash:
$the_path{$folders[0]}{$folders[1]}{$folders[2]}="somevalue";
But if the array is only:
#folders=(bin);
then i want the path to be:
$the_path{$folders[0]}="somevalue";
The problem is I dont know beforehand how long the array is gonna be, and I would really like to avoid making x if statements for that solution scales terribly.
How do I do this?
First, that's not how you define an array in Perl. You probably want to say
my #folders = ( 'temp', 'usr', 'bin' );
There's an old trick for making nested hash keys from a list:
my %the_path;
my $tmp = \%the_path;
foreach my $item( #folders ) {
$tmp->{$item} = { };
$tmp = $tmp->{$item};
}
This will result in a structure like the following:
$VAR1 = {
'temp' => {
'usr' => {
'bin' => {}
}
}
};
If you want to replace the empty hashref at the bottom-most level with a string, you can keep track of a count variable inside the loop.