1

I'm trying to do a function in Perl (see below) able verify the existence of each word in the hash (%emailWords) over the hash (%cache). If the word from %emailWords is not in the %cache but the method belongsTo returns true %cache hashmap must be updated with the new $word. However, altough I pass the %cache hashmap as reference, the value outside the function is empty.

sub computeConceptsUntilLevel{

    my $hashRef = shift;
    my $wordnetRef = shift;
    my $cacheRef = shift;
    my $deepLevel = shift;

    my %emailWords = %{$hashRef};
    my @conceptsArray = @{$wordnetRef};
    my %cache = %{$cacheRef};

    my @toret = ();

    for (my $i=0;$i<$deepLevel;$i++){
        my %conceptsMap = ();
        foreach my $concept (@{$conceptsArray[$i]}){
            foreach my $word (keys %emailWords) {
                print "[$i]->Palabra: $word - Concepto: $concept\n";
                if (wordHasConcept(\%cache,$word,$concept,$i)){
                    push @{$conceptsMap{$concept}}, $word;
                }else{
                    if (defined($word) and belongsTo($word,$concept)){
                        push @{$conceptsMap{$concept}}, $word;
                        push @{$cache{$word}{$i}}, $concept;
                    }
                }
            }
        }
        $toret[$i] = \%conceptsMap;
    }
    return @toret;
}

Inside the function the %cache looks like:

$VAR1 = {
          'cat' => {
                     '1' => [
                              'physical_entity#n#1'
                            ],
                     '0' => [
                              'entity#n#1'
                            ]
                   },
          'duck' => {
                      '1' => [
                               'physical_entity#n#1'
                             ],
                      '0' => [
                               'entity#n#1'
                             ]
                    }
        };

But outside is $VAR1={}.

What I'm doing wrong?

2 Answers2

2

First of all, you didn't pass a hash by reference; you passed a reference to a hash.

While $VAR1, $_[2] and $cacheref all reference the same hash, it's not %cache. Changing %cache won't affect the hash referenced by those (though changing variables referenced by values of %cache may change variables referenced by values of %$cacheref).

Simplest solution: Stop doing all those wasteful copies!

sub computeConceptsUntilLevel{

    my $emailWords    = shift;
    my $conceptsArray = shift;
    my $cache         = shift;
    my $deepLevel     = shift;

    my @toret = ();

    for (my $i=0;$i<$deepLevel;$i++){
        my %conceptsMap = ();
        foreach my $concept (@{$conceptsArray->[$i]}){
            foreach my $word (keys %$emailWords) {
                print "[$i]->Palabra: $word - Concepto: $concept\n";
                if (wordHasConcept($cache,$word,$concept,$i)){
                    push @{$conceptsMap{$concept}}, $word;
                }else{
                    if (defined($word) and belongsTo($word,$concept)){
                        push @{$conceptsMap{$concept}}, $word;
                        push @{$cache->{$word}{$i}}, $concept;
                    }
                }
            }
        }
        $toret[$i] = \%conceptsMap;
    }
    return @toret;
}

If you want to allow

computeConceptsUntilLevel(..., ..., my $cache, ...)

Change

my $cache = shift;

to

my $cache = ( shift //= {} );

This is possible because arguments are (always) passed by reference in Perl.


for my $i (0 .. $deepLevel-1)

is clearer and more efficient than

for (my $i=0;$i<$deepLevel;$i++){

push @toret, \%conceptsMap;

is clearer than

$toret[$i] = \%conceptsMap;

my @toret;
my %conceptsMap;

are less wasteful and cluttered versions of

my @toret = ();
my %conceptsMap = ();
ikegami
  • 367,544
  • 15
  • 269
  • 518
0

To understand better how to work with perl data structures look here

The problem in your program lives here:

my %cache = %{$cacheRef};

Here you create copy of cache inside your sub. So any keys you add at this sub will live only at %cache and %$cacheRef will intact.

In modern perl you may do:

my \%cache = $cacheRef;

This will alias %cache to $cacheRef.
Or do some replaces in your code:

  1. Remove this my %cache = %{$cacheRef};
  2. Instead of \%cache use $cacheRef
  3. Instead of $cache{$word}{$i} use $cacheRef->{$word}{$i} \%cache
Community
  • 1
  • 1
Eugen Konkov
  • 22,193
  • 17
  • 108
  • 158