gitarwmn has asked for the wisdom of the Perl Monks concerning the following question:
Thank everyone! I got it working now. I learn just as much from the monks as I do from class.
Hello,
I'm taking a class in perl and this is my first time working in detail with hashes. One of my assignments is to prompt the user for a word, and then output the Scrabble score for that word. I'm using grep to find the letter in the hash and then give me the output but it doesn't seem to be working. Any advise on this would be helpful.
Thanks
Here is my code so far
my %pay = ( A => 1, F => 4, K => 5, P => 3, U => 1, Z => 10,
B => 2, G => 2, L => 1, Q => 10, V => 4,
C => 3, H => 4, M => 3, R => 1, W => 4,
D => 2, I => 1, N => 1, S => 1, X => 8,
E => 1, J => 8, O => 1, T => 1, Y => 4
);
my $word;
my @word;
my @result;
print "Enter word to calculate.\n";
print "\nPress 'q' to quit\n";
while (){
print "\nWord?\n";
chomp ($word = <STDIN>);
@word = split("", $word); # split up $word
if ($word eq 'q'){
print "Quiting program";
last;
}
foreach my $w (@word){
@result = grep {defined $w} @pay{@word};
}
for my $r (@result){
print "[$_]\n";
}
}
Re: Homework help
by radiantmatrix (Parson) on Oct 29, 2004 at 19:15 UTC
|
Firstly, grep is excessive. Since your keys are upper-case, simply use uc to upper-case things before matching.
#!/usr/bin/perl
use strict;
use warnings;
my @words;
while (<STDIN>) { push @words, split /\s/ }
my %wordscore = scrabble_score(@words);
for (sort keys %wordscore) {
print qq/"$_" = $wordscore{$_} points\n/;
}
sub scrabble_score {
my %value = ( A => 1, F => 4, K => 5, P => 3, U => 1, Z => 1
+0,
B => 2, G => 2, L => 1, Q => 10, V => 4,
C => 3, H => 4, M => 3, R => 1, W => 4,
D => 2, I => 1, N => 1, S => 1, X => 8,
E => 1, J => 8, O => 1, T => 1, Y => 4
);
my %score;
for (@_) {
my @letter = split "", uc $_;
my $word = $_;
$score{$word} = 0;
for (@letter) { $score{$word}+=$value{$_} }
}
return %score;
}
Note that it reads STDIN until EOF -- Ctrl-D on Unix/Linux and Ctrl-Z on Windows (followed by Enter on some of both types of systems). Or you can pipe in a word list... but you needn't have one word per line, as long as you use space between them.
Also, because of how hashes function, it will only print one instance of each word.
By the way, you aren't checking to make sure the word is possible in Scrabble: there are a limited quantity of each letter... :)
radiantmatrix
require General::Disclaimer;
"Users are evil. All users are evil. Do not trust them. Perl specifically offers the -T switch because it knows users are evil." - japhy
| [reply] [d/l] |
Re: Homework help
by ikegami (Patriarch) on Oct 29, 2004 at 19:23 UTC
|
I found two problems:
You're printing $_, but $r contains what you want to print.
You should uppercase the input (using uc()). Hash lookups are case-sensitive.
I also have two recommendations:
It would make more sense if if ($word eq 'q'){ appeared before the split.
Something's really odd with your foreach+grep. In fact, there's no need for a foreach at all. Get rid of the foreach loops and use just @result = grep {defined $_} @pay{@word}; (which simplifies to @result = grep defined, @pay{@word};). You'd be smart if you went and reread some examples using grep, and make sure you know what @hash{list} returns.
Now you just have to sum up the elements of @result with something like my $sum = 0; $sum += $_ foreach @result;.
You could even remove the grep, replace it with a for/foreach loop that does both the validation and the summing. In fact, that's probably a good idea.
Here's how I'd do it:
my %scores = (
map { $_ => 1 } qw( A E I L N O R S T U ),
map { $_ => 2 } qw( B D G ),
map { $_ => 3 } qw( C M P ),
map { $_ => 4 } qw( F H V W Y ),
map { $_ => 5 } qw( K ),
map { $_ => 8 } qw( J X ),
map { $_ => 10 } qw( Q Z ),
);
print("Find the Scrabble value of a word.$/$/");
for (;;) {
print("Enter a word (or just Enter to quit): ");
local $_ = <STDIN>;
last unless defined;
chomp;
last unless length;
$_ = uc($_);
my $sum = 0;
$sum += $scores{$1} while (/([A-Z])/g);
print("Scrabble value: $sum$/$/");
}
| [reply] [d/l] [select] |
|
%scores = (
map { $_ => 1 } qw( A E I L N O R S T U ),
map { $_ => 2 } qw( B D G ),
map { $_ => 3 } qw( C M P ),
map { $_ => 4 } qw( F H V W Y ),
map { $_ => 5 } qw( K ),
map { $_ => 8 } qw( J X ),
map { $_ => 10 } qw( Q Z ),
);
print join($", sort keys %scores), $/;
__END__
10 2 3 4 5 8 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Since you're filtering for letters, the numbers don't get picked up, but the maps are having a side-effect mapping of the other maps below them. Parens around the maps seems to fix it.
I also learned something from your example. I would have predicted that the last unless defined would not work right on empty input b/c $_ still contains a "\n" which would be defined. I would have been wrong.
| [reply] [d/l] |
|
I would have predicted that the last unless defined would not work right on empty input b/c $_ still contains a "\n"
It doesn't work on empty input. It works on eof/error. It's there to prevent the next two lines from giving "undefined value" warnings under use warnings. The last unless length that follows is what detects the empty string (since chomp removed the "\n" by then).
| [reply] [d/l] [select] |
|
# perl -l
my %scores = (
map { $_ => 1 } qw( A E I L N O R S T U ),
map { $_ => 2 } qw( B D G ),
map { $_ => 3 } qw( C M P ),
map { $_ => 4 } qw( F H V W Y ),
map { $_ => 5 } qw( K ),
map { $_ => 8 } qw( J X ),
map { $_ => 10 } qw( Q Z ),
);
print "$_ => $scores{$_}" for keys %scores;
__END__
S => 1
T => 1
N => 1
K => 1
Y => 1
2 => 1
E => 1
Z => 1
J => 1
W => 1
B => 1
H => 1
D => 1
I => 1
10 => 1
G => 1
U => 1
F => 1
V => 1
Q => 1
M => 1
C => 1
L => 1
A => 1
O => 1
3 => 1
X => 1
P => 1
8 => 1
4 => 1
R => 1
5 => 1
Perhaps this is a good time for parentheses?
my %scores = (
map( { $_ => 1 } qw( A E I L N O R S T U ) ),
map( { $_ => 2 } qw( B D G ) ),
map( { $_ => 3 } qw( C M P ) ),
map( { $_ => 4 } qw( F H V W Y ) ),
map( { $_ => 5 } qw( K ) ),
map( { $_ => 8 } qw( J X ) ),
map( { $_ => 10 } qw( Q Z ) ),
);
print "$_ => $scores{$_}" for keys %scores;
__END__
| [reply] [d/l] [select] |
Re: Homework help
by Popcorn Dave (Abbot) on Oct 29, 2004 at 19:33 UTC
|
| [reply] |
Re: Homework help
by FoxtrotUniform (Prior) on Oct 29, 2004 at 18:45 UTC
|
This would be a great place to use List::Util's sum: use map to turn an array of characters into an array of points values, then add it all up.
| [reply] [d/l] [select] |
Re: Homework help
by waswas-fng (Curate) on Oct 29, 2004 at 19:27 UTC
|
use strict;
use warnings;
my %pay = ( A => 1, F => 4, K => 5, P => 3, U => 1, Z => 10,
B => 2, G => 2, L => 1, Q => 10, V => 4,
C => 3, H => 4, M => 3, R => 1, W => 4,
D => 2, I => 1, N => 1, S => 1, X => 8,
E => 1, J => 8, O => 1, T => 1, Y => 4
);
my $word;
my @word;
print "Enter word to calculate.\n";
print "\nPress 'q' to quit\n";
while (){
my $total_points = 0;
print "\nWord: ";
chomp ($word = uc(<STDIN>));
die "Quiting program...\n" if ($word eq 'Q');
foreach my $letter (split("", $word)){
$total_points += $pay{"$letter"} if defined $pay{"$letter"} ;
printf "Letter \"%s\" = %d points. Total points: %d\n", $lette
+r,$pay{"$letter"}, $total_points;
}
}
Output:
Enter word to calculate.
Press 'q' to quit
Word: test
Letter "T" = 1 points. Total points: 1
Letter "E" = 1 points. Total points: 2
Letter "S" = 1 points. Total points: 3
Letter "T" = 1 points. Total points: 4
Word: q
Quiting program...
| [reply] [d/l] [select] |
|
|