Home > Archive > PERL Beginners > January 2006 > Reference question
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Reference question
|
|
| umptious@gmail.com 2006-01-24, 9:56 pm |
| Hi -
I'm writing a Markov Chain program like the one in the Kernigahn and
Pike book (which I don't have handy)... but that probably doesn't
matter much. What I'm struggling to understand is why neither the pair
of lines marked #1 nor the pair following marked #2 work, but the
following - and to me seemingly equivalent - lines do.
I'm really puzzled by this one.... Thanks if you can explain!
# sum (ref to array of numbers) => number
sub sum {
my $ar = $_[0];
my $r = 0;
for (@$ar) {
$r += $_;
}
return $r;
}
# make_chain(file_name) => ref to hash containing markov chain
sub make_chain {
open FILE, $_[0] or die "Can't find file";
# MAKE HASH SO THAT KEYS ARE EACH WORD PAIR AND VALUE IS
# HASH{WORD =>NUM TIMES FOUND AFTER PAIR}
my %h = ();
my ($a, $b) = ("", "");
# For each word..
for my $line (<FILE> ) {
chomp($line);
for my $word (split / /, "$line") {
# Make key from preceding words
my $k = join "~", $a, $b;
# Update hash
if (defined $h{$k}) {
my $h2 = $h{$k};
if (defined $$h2{$word}) {
$$h2{$word}++;
} else {
$$h2{$word} = 1;
}
} else {
$h{$k} = {$word => 1};# was allowed to use just "k" -
no $!
}
# Update preceding words
($a, $b) = ($b, $word);
}
}
# NORMALIZE
for my $k (keys %h) {
my $h2 = $h{$k};
#1 my $v = \ values %$h2;
#1 my $sum = sum($v);
#2 my $v = \ (values %$h2);
#2 my $sum = sum($v);
my @v = values %$h2;
my $sum = sum(\@v);
for my $w (keys %$h2) {
$$h2{$w} /= $sum;
}
}
return \%h;
}
| |
| Paul Lalli 2006-01-25, 7:55 am |
| umptious@gmail.com wrote:
> I'm writing a Markov Chain program like the one in the Kernigahn and
> Pike book (which I don't have handy)... but that probably doesn't
> matter much. What I'm struggling to understand is why neither the pair
> of lines marked #1 nor the pair following marked #2 work, but the
> following - and to me seemingly equivalent - lines do.
> #1 my $v = \ values %$h2;
> #1 my $sum = sum($v);
>
> #2 my $v = \ (values %$h2);
> #2 my $sum = sum($v);
>
> my @v = values %$h2;
> my $sum = sum(\@v);
Please work on partitioning down your problem. Your problem amounts to
"why can't I take a reference to the values returned by a hash?".
There was no need for all the extraneous code. Creating a
short-but-complete script that demonstrates your problem should be one
of the first debugging techniques you employ.
The problem, I believe, is that values() behaves differently in list
context and scalar context. In a scalar context, values() returns the
number of values in the hash. You are taking a reference to this
number in both #1 and #2, because you are assigning to a scalar
variable (the \ does not change the context of the function
evaluation). In the "working" code, you are assigning values() to an
array, which evaluates the function in a list context, and so it
returns the actual values of the hash.
If you want to get a reference without the extraneous temporary
variable, use a reference construct that imposes list context:
my $v = [ values %$h2 ];
my $sum = sum($v);
Hope this helps,
Paul Lalli
| |
| umptious@gmail.com 2006-01-25, 6:57 pm |
|
> Please work on partitioning down your problem. Your problem amounts to
> "why can't I take a reference to the values returned by a hash?".
You're quite right.
> The problem, I believe, is that values() behaves differently in list
> context and scalar context. In a scalar context, values() returns the
> number of values in the hash. You are taking a reference to this
> number in both #1 and #2, because you are assigning to a scalar
> variable (the \ does not change the context of the function
> evaluation). In the "working" code, you are assigning values() to an
> array, which evaluates the function in a list context, and so it
> returns the actual values of the hash.
>
> If you want to get a reference without the extraneous temporary
> variable, use a reference construct that imposes list context:
>
> my $v = [ values %$h2 ];
> my $sum = sum($v);
>
> Hope this helps,
It definitely does help, but I still feel there are general rules I'm
missing about either Perl references or complex Perl statements... I'm
fine until things reach a certain level of complexity, then I have to
break things into stages in a way I don't in other languages. I have
another reference problem, which is:
my $choices = $$chain{$k};
my %choices2 = %$choices;
Where $chain is a ref to a hash, whose keys are strings, and whose
values are refs to another hash, whose keys are strings and values are
numbers (hey, it'sa Markov chain - what do you expect?)
Again, I can't see how to get this to a one liner. I've tried lots of
things - I won't bother you with them.
This isn't a problem in itself, but it scares me that I'm missing
something. Suggestions, explanations, recommended reading?
| |
| Paul Lalli 2006-01-25, 6:57 pm |
| umptious@gmail.com wrote:
>
> It definitely does help, but I still feel there are general rules I'm
> missing about either Perl references or complex Perl statements... I'm
> fine until things reach a certain level of complexity, then I have to
> break things into stages in a way I don't in other languages. I have
> another reference problem, which is:
>
> my $choices = $$chain{$k};
> my %choices2 = %$choices;
>
> Where $chain is a ref to a hash, whose keys are strings, and whose
> values are refs to another hash, whose keys are strings and values are
> numbers (hey, it'sa Markov chain - what do you expect?)
>
> Again, I can't see how to get this to a one liner. I've tried lots of
> things - I won't bother you with them.
That's generally a mistake. Telling us what you tried and describing
how it failed are generally necessary components to helping you correct
your misunderstandings.
> This isn't a problem in itself, but it scares me that I'm missing
> something. Suggestions, explanations, recommended reading?
I'm as to what it is you actually want to do. You want to
assign %choices2 to be a hash with the same key/value pairs as the hash
referenced by $$chain{$k} ?
my %choices2 = %{$$chain{$k}};
You should read:
perldoc perlreftut
And pay special attention to Make Rule 1, Make Rule 2, Use Rule 1, Use
Rule 2, Arrow Rule, and The Rest
In general, you dereference a reference by surrounding it in { } and
prepending the appropriate sigil ($ for scalar reference, @ for array
reference, % for hash reference). You can only remove the { } if the
thing inside them is a single simple scalar variable. So, if $chain is
your hash reference, then
%{$chain} is your hash, and
${$chain}{$k} is the value of that hash at key $k
Most people generally employ the arrow rule at this point to change
this to
$chain->{$k}
Now we have $chain->{$k} being a reference to an "inner" hash. So to
dereference it:
%{$chain->{$k}}
This is what we can assign to %choices2 to make the copy:
%choices2 = %{$chain->{$k}}
Note that the { } are *required* here, because what's inside them is
not a single simple scalar variable.
Hope this helps,
Paul Lalli
| |
| umptious@gmail.com 2006-01-25, 6:57 pm |
|
Paul Lalli wrote:
> umptious@gmail.com wrote:
>
> That's generally a mistake. Telling us what you tried and describing
> how it failed are generally necessary components to helping you correct
> your misunderstandings.
Well, what I want is to get a the hash for a key in the markov chain -
ie the hash the ref for which is the value in %$chain. The following
two liner works:
When you say
[color=darkred]
> I'm as to what it is you actually want to do. You want to
> assign %choices2 to be a hash with the same key/value pairs as the hash
> referenced by $$chain{$k} ?
then you're precisely right, and this is what I get. But I can't work
out what the one liner is.
> my %choices2 = %{$$chain{$k}};
Ah. I tried %$$chain{$k} and {$$chain{$k}} and {%$$chain{$k}};. Let me
se if I understand:
- $$chain{$k} is a ref to hash
- % gets the hash
- {} forces hash rather than scalar context
But what I don't get is the % goes outside the {} instead of the other
way around - hopefully those docs will explain.
Based on my experience, almost all the difficulty in learning Perl
comes from references - especially dereferencing!
Thanks, Paul!
>
> You should read:
> perldoc perlreftut
>
> And pay special attention to Make Rule 1, Make Rule 2, Use Rule 1, Use
> Rule 2, Arrow Rule, and The Rest
>
> In general, you dereference a reference by surrounding it in { } and
> prepending the appropriate sigil ($ for scalar reference, @ for array
> reference, % for hash reference). You can only remove the { } if the
> thing inside them is a single simple scalar variable. So, if $chain is
> your hash reference, then
> %{$chain} is your hash, and
> ${$chain}{$k} is the value of that hash at key $k
> Most people generally employ the arrow rule at this point to change
> this to
> $chain->{$k}
>
> Now we have $chain->{$k} being a reference to an "inner" hash. So to
> dereference it:
> %{$chain->{$k}}
> This is what we can assign to %choices2 to make the copy:
> %choices2 = %{$chain->{$k}}
>
> Note that the { } are *required* here, because what's inside them is
> not a single simple scalar variable.
>
> Hope this helps,
> Paul Lalli
| |
| umptious@gmail.com 2006-01-25, 6:57 pm |
| Arrrggghhhhhhh - got it. Looks like I was taught just the short hand
form. Thanks for the reference, Paul -I'm looking through the other
docs there as well.
|
|
|
|
|