I've been working on a card game that gets your deck and picks 1 out of three powers cards but whenever I try to run it, it gives me an error
Error:
Error: In call to CPUser::addCards(), was given too many arguments; it expects 1 at Server/CPUser.pm line 427.
Line 427:
if($intItem == 821){ #Adds Card Jitsu Cards for Classic
$self->addCards($self->buildStarterDeck);
}
and this the buildStarterDeck Method
method buildStarterDeck {
sub get_cards;
my (#stackone, #stacktwo) = get_cards;
sub get_cards
{
my #start_cards = (1, 6, 9, 14, 17, 20, 22, 23, 26);
my #power_cards = (73, 81, 89);
#power_cards = $power_cards[rand #power_cards];
return (#start_cards, #power_cards);
}
}
The addCard method is empty since I've been trying to figure out this error and I couldn't get any luck.
The problem is in your definition of addCards which you persist in hiding from us despite multiple requests to see it. It must look something like this
method addCards($param) {
...;
}
But you are passing it a ten-element list in $self->addCards($self->buildStarterDeck) so the given too many arguments error is raised. You don't explain what you want it to do, but something like this is probably more appropriate
method addCards(#cards) {
...;
}
You really shouldn't declare subroutines inside other subroutines. It doesn't limit the scope of the inner subroutine, but you can create a closure over variables declared in the outer subroutine that doesn't work properly
Also bearing in mind zdim's warning from the comments, your code should look more like this
use strict;
use warnings 'all';
use Method::Signatures;
method buildStarterDeck {
my #stack = get_cards;
}
sub get_cards {
my #start_cards = (1, 6, 9, 14, 17, 20, 22, 23, 26);
my #power_cards = (73, 81, 89);
$power_card = $power_cards[rand #power_cards];
return (#start_cards, $power_card);
}
Related
I am trying to write some classes with Perl 6 just for testing out Perl 6 classes and methods.
Here is the code:
class human1 {
method fn1() {
print "#from human1.fn1\n";
}
}
class human2 {
method fn1() {
print "#from human2.fn1\n";
}
}
my $a = human1.new();
my $b = human2.new();
$a.fn1();
$b.fn1();
print "now trying more complex stuff\n";
my $hum1_const = &human1.new;
my $hum2_const = &human2.new;
my $c = $hum2_const();
$c.fn1();
Essentially I want to be able to use either the human1 constructor or human2 constructor to be able to build $c object dynamically. But I'm getting the following error:
Error while compiling /usr/bhaskars/code/perl/./a.pl6
Illegally post-declared types:
human1 used at line 23
human2 used at line 24
How do I create $c using the function pointers to choose which constructor I use?
I think this is a case of an LTA error. What I understand you want to achieve, is a lambda that will create a new human1 or human2 object for you. The way you do that is not correct, and the error it causes is confusing.
my $hum1_const = -> { human1.new };
my $hum2_const = -> { human2.new };
would be a correct way of doing this. Although, I would consider this a bit of an obfuscation. Since human1 and human2 are already constants, you can assign them to a variable, and then just call new on that:
my $the_human = $condition ?? human1 !! human2;
my $c = $the_human.new;
$c.fn1;
Does that make sense?
To get a “reference” to .new you have to use the meta object protocol.
Either .^lookup, or .^find_method.
my $hum1-create = human1.^find_method('new');
That is still not quite what you are looking for, as methods require either a class object or an instance, as their first argument.
my $c = $hum1-create( human1 );
So you would probably want to curry the class as the first argument to the method.
my $hum1-create = human1.^find_method('new').assuming(human1);
my $c = $hum1-create();
Note that .assuming in this case basically does the same thing as
-> |capture { human1.^find_method('new').( human1, |capture ) }
So you could just write:
my $hum1-create = -> |capture { human1.new( |capture ) }
Or if you are never going to give it an argument
my $hum1-create = -> { human1.new }
Also you can store it in a & sigiled variable, so you can use it as if it were a normal subroutine.
my &hum1-create = human1.^find_method('new').assuming(human1);
my $c = hum1-create;
I am getting a warning as:
Use of uninitialized value in numeric eq (==) at script.pl line 53; line 53 isif statement`
My code snippet:
foreach(#array)
{
push #gene2refseq, $_;
}
foreach(#::ids)
{
if($_ == $gene2refseq[1])
{
push #::prot_gi, $gene2refseq[6];
}
}
Now if I declare $gene2refseq[1] before foreach(#::ids) the same error persists; but if I initialize it before foreach(#::ids) viz. $gene2refseq[1] = 0 it didn't give the error but also no results; as the value is initialized to 0.
I think I am initializing it at a wrong place, but then where have I to initialize it? As I can't initialize it before or in foreach(#array)
Disclaimer: I am not very good with use warnings and use strict
Edit: Solved
Thanks for the help; I just declared the #gene2refseq and initialized $gene2refseq[1] = 0 before the foreach(#array), and it worked fine.
Also thanks for correcting me on foreach usage.
A question:
What if I have to access multiple indexes of an array? Do I have to initialize them all? As here I need to access only a single index, so I initialized it.
If #gene2refeq is empty and #array is empty before the start of that code snippet, then #gene2refeq will be empty by line 53.
To find out, print the contents of #array and #gene2refeq. It's also possible #::ids contains uninitialized values, check that too. Add a separator so you can see what's in them.
print "\#array is ".join(", ", #array)."\n";
print "\#gene2refeq is ".join(", ", #gene2refeq)."\n";
print "\#::ids is ".join(", ", #::ids)."\n";
As to your question about when to initialize things, there's a difference between declaring a variable and initializing it. my and our declare a variable to exist lexically and globally respectively. Then you can populate (initialize) it.
The basic pattern of your program might look something like this.
my #ids;
...code to populate #ids...
my #gene2refeq;
...code to populate #gene2refeq...
my #array;
...code to populate #array...
push #gene2refeq, #array;
die "\#gene2refeq is not long enough, ".join(", ", #gene2refeq)
unless #gene2refeq >= 7;
my #prot_gi;
foreach my $id (#ids) {
if($id == $gene2refseq[1])
{
push #prot_gi, $gene2refseq[6];
}
}
A few other points. That first foreach loop is better written as simply...
push #gene2refseq, #array;
Using #::ids and #::prot_gi is odd. That's shorthand for getting the global variable #ids in the main package. It shouldn't be necessary and smells like cargo culting.
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}();
We have observed strange effects when using $return variable in code like $return = foo($something);. Is $return variable somehow special because of the return name?
According to the Perl documentation, no.
No it is not perl special. But some module my export it and this may provide unexpected behavior.
see This tutorial
There's nothing special about a variable named $return. That said, writing
my $return = foo($something);
return $return;
is not the same as writing
return foo($something);
The first form puts the call to foo() in scalar context. The latter will propagate the context from the caller. This could cause foo() to behave differently. Compare:
sub foo { return localtime }
sub bar { my $x = foo(); return $x }
sub baz { return foo() }
say join ', ', bar(); # Thu May 26 08:24:59 2011
say join ', ', baz(); # 59, 24, 8, 26, 4, 111, 4, 145, 1
This happens because in scalar context, localtime returns the time formatted as a string but in list context it returns a list of values for seconds, minutes, hours, etc.
The concept of context is unique to Perl. To learn more about it, see perlsub.
I have index.pl and subs.pl. When I run the program, the user inserts the date of birth and then it is passed to the getage() subroutine in subs.pl, which has many subroutines.
getage() than implicitly calls another subroutine called validate() which validates the date entered by user.
When I run the index.pl and the user enters the date as 03-04-2005, the following error comes out:
can't modify non-lvalue subroutine call at subs.pl line 85, <> line 1
At 85th line of subs.pl I have:
list(my $val,my #value) = validate($dob);
validate() returns a message and the date($dob) which is sent from getage().
Some code from validate():
sub validate {
my $dob = shift;
my $error;
my #test;
#test = split("-",$dob);
if (!#test) {
$error = "date separator should be - ";
return ($error,#test);
}
...
The solution seems to be:
my ($val, #value) = validate($dob);
based on my intuitive understanding of what that code intended - but I can't be certain till you provide some more context (what does validate() return, what does list() mean?)
If you meant list() as a means to force $val and #value into a list, you merely need to enclose both in parenthesis: ($val, #value) to do that
An lvalue is a variable you can modify. (one that can be on the left side of an assignment, hence the name). In most circumstances, a value returned by a sub is not one you can modify.
In your example, you are attempting exactly that: assigning the return value of validate($dob) to the nonmodifiable return value of list($val, #value).
in line
list(my $val,my #value) = validate($dob);
remove 'list' and it works fine
ie
(my $val,my #value) = validate($dob);
thanks to Kayra and others who answered