Re: Control Structure problem, mistake can't be found
by broomduster (Priest) on Aug 23, 2008 at 00:21 UTC
|
One more tip for how to debug this kind of thing....
After you set $guess (whether you read it from STDIN or not), but before the line
if ($guess eq $correct[$i])
you can see what the value of $guess is by doing
print "[$guess]";
Note the square brackets ( [ and ] ). They are important here because they show where the value starts and ends. So if the value of $guess is Yellow\n, you get
[Yellow
]
and you see clearly that you have a \n at the end (which you may not want).
| [reply] [d/l] [select] |
Re: Control Structure problem, mistake can't be found
by FunkyMonk (Chancellor) on Aug 22, 2008 at 23:08 UTC
|
Where does the contents of $guess come from? If it's input from the user via the keyboard, or from a file, it's likely to have a newline after it. Try matching using $guess eq "$correct[$i]\n" or chomp your input before comparing.
| [reply] [d/l] |
Re: Control Structure problem, mistake can't be found
by gone2015 (Deacon) on Aug 23, 2008 at 00:33 UTC
|
As a general point this game isn't going to be easy, even if you know the expected answers, the:
if ($guess eq $correct[$i])
is requiring the guess to have the exact capitalisation of the answer and won't tolerate extra white-space or any other deviation from the expected string. Related to that is what FunkyMonk has already suggested, which is the possible presence of a newline on the end of $guess. Preprocessing $guess and/or using a form of regular expression to do the matching will definitely help.
In passing I notice: @incorrect = (@incorrect, $guess);
which might be better written as: push @incorrect, $guess ;
and also: my $grade = 100 - length(my @incorrect) * 33.3;
which I rather doubt is what you meant. It sets $grade to 66.7, because length doesn't give the length of an array, it gives the length of a string in characters. So why does it give an answer at all when asked for the length of an array ?... well, length(my @incorrect) gives 1 because the value of my @incorrect is an empty array, which when evaluated in scalar context gives the number 0, which when converted to a string is '0', which is 1 character long... This is Perl all over :-)
To get the number of entries in an array you just evaluate the array in scalar context... so: scalar(@incorrect) is probably what you wanted.
Finally, at the risk of being presumptious, if you are new to programming in general, I'd really advise you to study another language.
It's true that you can hack about in Perl and do some wonderful stuff quite economically. But, not only is it possible to pick up some truely disgusting habits, you need a grounding in something more formal if you are to really understand what Perl is doing.
Look at it this way, compared to most languages English has apparently little structure and few rules. This makes it relatively easy to learn and use, at least to get by. However, it is also hard to really master the subtleties of English, with all its irreglarity, conventions, idioms and other oddities; and to do that you do need some grammer and some formal teaching. Perl is like that.
| [reply] [d/l] [select] |
|
...not only is it possible to pick up some truely disgusting habits, you need a grounding in something more formal if you are to really understand what Perl is doing.
++, oshalla, for your other helpful advice, but not without taking exception to the quoted observation!
I have no real quibble with your "possible" re "truly disgusting habits" (NB: in English there's no 'e' in 'truly.') except to note that's pretty much also "possible" to write effectively - if inelegantly - despite misspellings -- whether writing narrative or text. Conversly "proofs" of almost any proposition can be so badly conceived or structured as to be worthless despite having the appearance of proper form or even superficial plausibility.
However, IMO, to suggest that it's not possible to understand "what Perl is doing" without formal study of another language for "grounding" is extreme, to say nothing of "unproven!"
| [reply] |
|
Yes, I can never correctly spell 'truly' (correction: I can seldom spell it). I am ashamed. What can I say: without the 'e' it just looks to me as if it's had an unfortunate accident with something sharp -- an object lesson in what happens when you run with scissors, perhaps ? It also looks almost as if it should be pronounced with a short 'u', but for that, I have to admit, two 'l's would be required -- as in 'cully'.
Of course many other words that end in 'e' don't drop it when the adverb is formed -- 'insanely', 'obscenely', 'approximately', 'effectively'. But that may be because they don't end in two vowels. So there's 'brusquely' and 'opaquely', but I guess the 'u' isn't really a vowel in this context. What about 'eerily', and of course 'duly' -- the later is clearly of a piece with 'truly'. But, lest we conclude there's a simple rule here, there's 'vaguely' and 'freely' -- admittedly the sound of the 'ue' in 'vague' is quite different from that in 'true', which may (or may not) be significant. Similarly, in the case of 'free' it may be that the 'ee' is effectively a compound vowel ?
English: what can I say ? Gosh, what a glorious mess !
Anyway, returning to what I suppose is the real matter: I think that to "really understand what Perl is doing" you do need some more formal grounding -- highlighting the stress I intended on the "really".
For instance, consider the following:
my @rows = () ;
while (my $line = <SHEET>) {
chomp($line) ;
my @columns = split /\s+/, $line ;
push @rows, \@columns ;
} ;
What do we think is going on here ?
- declare the (one dimensional) array @rows and ensure it is initialised empty.
- run a while loop using various pieces of commonly used Perl magic to step line by line through the file referred to by SHEET, declaring the working variable $line for the duration of the while loop
- chomp($line) removes the line ending from the input line (unless you're running on a *nix type system and the file came from winders, in which case it tends to remove half the line ending, SO STAY AWAKE)
- declare the (one dimensional) array @columns for the remaining duration of the loop, and assign to it the result of splitting up the input line.
- push a reference to the array @columns onto the array @rows (we understand that in Perl arrays and lists are strongly related)
The result is intended to be a two-dimensional array (or what passes for one in Perl) of cells. And, indeed that's what happens.
Now, references are sophisticated, but just taking the code at face value, one might worry:
- if @columns only exists for the duration of the enclosing block, what does the @rows array contain after the block ?
- why does this work at all, given that it appears to push the same thing, \@columns, on to @rows each time around the loop ?
So, we all know that this works because my @columns doesn't declare an array, it declares something that contains a pointer to a data structure which contains the actual array and which lives on a heap. The \@columns doesn't really return a reference to @columns, it returns a copy of the pointer it contains. What's more, each time around the while loop @columns is reset before the new split /\s+/, $line is assigned to it, so each \@columns is different and refers to a different data structure on the heap -- each of which persists after the block (leaving reference counting and garbage collection to one side, for the time being).
Scanning back over the above, I observe it's hard to describe what's really going on, without using some fairly heavy computer science concepts. IMO, the study of more formal language(s) is the way to gain understanding of these and how they work.
This is only one example, but I believe it illustrates a general point. | [reply] [d/l] |
|
|
|
Re: Control Structure problem, mistake can't be found
by graff (Chancellor) on Aug 22, 2008 at 23:54 UTC
|
My bet is with the first reply -- if the user is entering answers at the keyboard, perl is reading the final "\n" that has to be typed in order to complete each input, and you have to use "chomp" to get rid of that.
As for making progress, you made the task harder for us (and less helpful for you) by showing us disjointed snippets and leaving things out. Next time, see if you can create a short script that can be run by itself and will demonstrate the problem you are having. For instance, your post could have been like this:
Why is it that the script below always prints "huh?", even though I definitely provide the input that should produce a different output?
#!/usr/bin/perl
use strict;
my @answers = ( 'me', 'you' );
print "Who needs more help, me or you? ";
my $response = <STDIN>;
if ( $response eq $answers[0] ) {
print "Good luck with that.\n";
}
elsif ( $response eq $answers[1] ) {
print "So help me!\n";
}
else {
print "huh?\n";
}
Given a situation like that, the vast majority of monks could tell you immediately, with no questions or doubt, that you need to "chomp" the input when reading from STDIN. (Or else they would tell you where to read about that in docs, tutorials, books, etc.) | [reply] [d/l] |
Re: Control Structure problem, mistake can't be found
by GrandFather (Saint) on Aug 23, 2008 at 02:36 UTC
|
If possible it is a really good idea to keep related data together so that it can be handled in a common fashion. Perl lets you do that by using structures like arrays of array and hashes etc. In your case it makes sense that you keep the question and answer pairs together. You could use an array for each pair, but it's easier to understand the code if you use a hash instead. Consider:
use strict;
use warnings;
my @quiz = (
{ask => "Name the definitive rock band..",
answer => "The Rolling Stones"
},
{ask => "What is their best song?",
answer => "Moonlight Mile"
},
{ask => "What is their best album?",
answer => "Sticky Fingers"
}
);
my @incorrect;
for my $question (@quiz) {
print "$question->{ask}: ";
my $guess = <STDIN>;
chomp $guess;
if (lc $guess eq lc $question->{answer}) {
print "You Rock!\n";
} else {
print "You Suck!\n"; # if incorrect, reply and save answer to
+ be calculated
push @incorrect, [$question, $guess];
}
}
my $grade = (@quiz - @incorrect) / @quiz * 100;
print "Grade: $grade\n";
Note that there is now no need for a counter to access the question and answer. Because each question and answer is paired it is trivial to add questions or change their order and know that the right answer is matched with each question.
Note too that now the entire question and answer are stored along with the answer given in the incorrect list. Also the grade uses the count of elements in the two arrays for the calculation so that a manually calculate constant isn't required - the grade calculation remains correct if the number of questions changes.
Perl reduces RSI - it saves typing
| [reply] [d/l] |
|
Just wondering, this syntax:
my @quiz = (
{ask => "Name the definitive rock band..",
answer => "The Rolling Stones"
},
{ask => "What is their best song?",
answer => "Moonlight Mile"
},
{ask => "What is their best album?",
answer => "Sticky Fingers"
}
);
it's object oriented Perl, right? Or is it just another way to give hash keys a value? I am only familiar with this type of syntax for the same thing:
$myquiz{"question_here"} = "answer here";
I've just never seen it done in this way, and I was wondering what the difference was (which when I looked it up, I thought I understood it to be "object oriented"), and if there is a benefit of chosing one over the other? | [reply] [d/l] [select] |
|
my @quiz = (...);
initializes an array from a list.
{...}
generates a reference to a hash. A reference is a "handle" that you can use to refer to something else. So:
my @quiz = ({...}, {...}, {...});
generates an array containing references to three hashes. Each hash is of the form:
{ask => "...", answer => "..."}
The => is the "fat comma". It's exactly like an ordinary comma, except that it pretends the (non-white space) sequence of characters to its left has quotes around it. ask => "..." is the same as 'ask', "...". The fat comma is a handy way of generating a list to initialize a hash and is often used as a heads up to indicate that two items in a list are a pair.
The intent is to generate a data structure comprised of an array containing a list of hashes where each hash represents a question and answer pair. Other information could be added to each hash such as a weighting for the question (so harder questions are worth more) for example:
my @quiz = (
{ask => "Name the definitive rock band..",
answer => "The Rolling Stones",
value => 10,
},
{ask => "What is their best song?",
answer => "Moonlight Mile"
value => 20,
},
{ask => "What is their best album?",
answer => "Sticky Fingers"
value => 5,
},
);
Perl reduces RSI - it saves typing
| [reply] [d/l] [select] |
|
| [reply] |
Re: Control Structure problem, mistake can't be found
by eosbuddy (Scribe) on Aug 22, 2008 at 23:22 UTC
|
It seems to work for me (cause I artificially made it to work). Some possible bugs are that your increment $i++; works only if expression is true - but this maybe what you intend to do as well. It would be helpful if you could post some errors:
#!/usr/bin/perl
use strict;
use warnings;
my @incorrect;
my @correct = qw / 1 2 3 4 5 6 7 8 9 10 /;
my $guess = 1;
my $i = 0;
if ($guess eq $correct[$i]) { # if guess is correct, reply
print "You Rock!\n";
$i++;
}
else {
print "You Suck!\n"; # if incorrect, reply and save answer to be calcu
+lated
@incorrect = (@incorrect, $guess);
}
| [reply] [d/l] [select] |
|
chomp($guess = <STDIN>);
at the top, before the piece I posted. When I run the program as is, this is my output:
Name the definitive rock band..
The Who
You Suck!
What is their best song?
Moonlight Mile
You Suck!
What is their best album?
Sticky Fingers
You Suck!
You Rock!
As you can see, no matter correct, or incorrect, always get "You Suck", then I have to enter an extra \n at the end to end the loop, after that, three lines later, it says "You Rock!, for no reason whatsoever. So, here's the whole code:
#!usr/bin/perl
use strict;
my @questions = ("Name the definitive rock band.." , "What is their be
+st song?" , "What is their best album?");
my @correct = ("The Rolling Stones" , "Moonlight Mile" , "Sticky Finge
+rs");
my $grade = 100 - length(my @incorrect) * 33.3;
my @incorrect;
my $i = 0;
my $guess;
# This program asks user three questions, then grades the user's ans
+wers.
while ($i <= scalar(@incorrect)) { # loop through questions
print $questions[$i] . "\n";
chomp($guess = <STDIN>);
$i++;
if ($guess eq $correct[$i]) { # if guess is correct, reply
print "You Rock!\n";
$i++;
} else {
print "You Suck!\n"; # if guess is incorrect, reply and save
+answer to be calculated
@incorrect = (@incorrect, $guess);
#while ($guess ne @correct[$i]) { # if guess is incorrect, pro
+mpt user to try again
# print "Try again!\n";
# chomp($guess = <STDIN>);
# $i++;
# } # end while
} # end if
} # end while
print $grade . "\n"; # calculate and print user's grade
print "\nRockstar Programming Inc. All Rights Reserved";
Note the loop commented out at bottom is a whole other topic, just included it so ya'll could see, because I certainly don't understand this output at all.
Also, eosbuddy, is this, I'm assuming a regular expression?
my @correct = qw / 1 2 3 4 5 6 7 8 9 10 /;
I do know what they are, lol, just not used to seeing that type at this point. I can't use them, but it is helpful to see how you put my data together to make it work.
But, does anyone understand this output I'm getting? I know my knowledge is very limited, but I just can't seem to place my mistake. There are, I'm sure several re-writes I could do, but I really want to understand why this won't work, to further my skill in the language, because theoretically I really think it should??
Also, please note that my output includes me entering answers as the user, I entered correct and incorrect to show that it didn't change the response.
Also, I am very much aware of how useless a trivia game is written this way, especially without any regular expressions. It's more of a learning exersise, to give me a small example of doing something on my own in Perl, and I also do know a bit more these days, but regardless of the fact that it is a useless game lol, I want to understand my mistake more for the experience than the game :) | [reply] [d/l] [select] |
|
$i++;
if ($guess eq $correct[$i]) { # if guess is correct, reply
Array indices in Perl start with 0. You are incrementing $i before you use it as an index to check the first answer. So the "correct" answer to the first question you ask should be "Moonlight Mile" ... ;-) | [reply] [d/l] [select] |
|
You are suffering from premature incrementation :)
And, for goodness' sake, use warnings;. This would have shown you that you incremented past the end of your @correct array (the first index of an array in perl is 0, not 1). I added these print statements to show the problem:
print "guess=$guess=\n";
print "correct[$i]=$correct[$i]=\n";
if ($guess eq $correct[$i]) { # if guess is correct, reply
print "You Rock!\n";
$i++;
| [reply] [d/l] [select] |
|
broomduster and toolic provided the solution, I just want to emphasize what broomduster said in another post in this thread: Learn to use print to debug your program.
If you had printed both of the comparision operands, $guess and $correct[$i] (and whatever else seemed interesting), you could have solved the riddle by yourself. And as you surely know, you best learn from things you find out yourself.
| [reply] [d/l] |
|
| [reply] |
|
|
|
As has already been pointed out by oshalla, you don't want to use length on an array, but rather scalar. A few other important notes:
| [reply] [d/l] [select] |
|
my @foo = qw(
key west florida
orange juice beverage
);
# this is identical to:
my @foo1 = (
'key', 'west', 'florida',
'orange', 'juice', 'beverage',
);
See perlop, specifically, the section on Quote and Quote-like Operators" for more info.
You should consider using push instead of recopying the array:
# @incorrect = (@incorrect, $guess);
push @incorrect, $guess;
When I was first learning, the single most useful piece of the documentation was the list of functions by category in perlfunc--although I used the version in my copy of Perl in a Nutshell. Anytime I felt like I was doing something awkward or that there might be a better way to do something, I'd look for a built in function to accomplish it.
Useless little toy programs are an excellent way to start. At the beginning of a big project I'll usually build several useless, little toy programs to test my ideas--only I've learned to call the toys "prototypes", because that sounds better. As a beginner, you are building the prototypes for ALL your future programs.
| [reply] [d/l] [select] |
Re: Control Structure problem, mistake can't be found
by Anonymous Monk on Aug 23, 2008 at 07:34 UTC
|
| [reply] |