For Programmers: Free Programming Magazines  


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.

Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2009 codecomments.com