Harvey Lin
Harvey Lin

Reputation: 794

Perl hash increment how does this work?

I have the following in the perl code:

$art_entities->{$rel_art_path}++;

I see that it is a post increment on a hash, how does it work? What would be something similar in Java or C#?

Upvotes: 1

Views: 2886

Answers (3)

ikegami
ikegami

Reputation: 386331

It creates an element in the hash if it doesn't exist, then increments its value by one[1].

For example,

my %counts;
++$counts{$_} for split(//, 'abracadabra');

produces

my %counts = (
   a => 5,
   b => 2,
   c => 1,
   d => 1,
   r => 2,
);

It's also useful for filtering out duplicates. For example,

my %seen;
my @uniq = grep !$seen{$_}++, split(//, 'abracadabra');

produces

my @uniq = qw( a b r c d );

Perl:

my %dict;

++$dict{$key};   # $dict{$key}++ in scalar context gets optimized into ++$dict{$key}.

C#:

Dictionary<string, int> dict = new Dictionary<string, int>();

int i;
dict.TryGetValue(key, out i);
dict[key] = i+1;

C#:

Dictionary<string, int> dict = new Dictionary<string, int>();

if (dict.ContainsKey(key))
   ++dict[key];
else
   dict[key] = 1;

Java:

Map<String, Integer> dict = new HashMap<String, Integer>();

Integer i = dict.get(key);
if (i == null) {
   map.put(key, 1);
} else {
   map.put(key, i+1);
}

  1. ++ has some "magical" behaviour if the variable being incremented contains a string (incrementing a into b, for example), but I don't think that's relevant here.

Upvotes: 4

Matt Jacob
Matt Jacob

Reputation: 6553

The ++ operator returns the value and then increments the variable by one. Hashes are just unordered collections of scalar values indexed by their associated string key; incrementing a scalar in a hash is no different from incrementing a scalar outside of a hash.

The following are functionally equivalent:

$hash{foo}++;
$hash_ref->{foo}++;
$foo++;

There's a little extra magic built in to Perl's auto-increment operator, though:

If you increment a variable that is numeric, or that has ever been used in a numeric context, you get a normal increment. If, however, the variable has been used in only string contexts since it was set, and has a value that is not the empty string and matches the pattern /^[a-zA-Z]*[0-9]*\z/, the increment is done as a string, preserving each character within its range, with carry:

print ++($foo = "99");    # prints "100"
print ++($foo = "a0");    # prints "a1"
print ++($foo = "Az");    # prints "Ba"
print ++($foo = "zz");    # prints "aaa" 

undef is always treated as numeric, and in particular is changed to 0 before incrementing (so that a post-increment of an undef value will return 0 rather than undef).

Java and C# both have increment operators, but as far as I know, they're not nearly as magical as Perl's. Java's HashMap provides get() and put() methods, so you don't get direct access to the underlying data:

map.put(key, map.get(key) + 1);

C# collections use square brackets, so if you had a Dictionary, for example:

dict["foo"]++;

Upvotes: 4

Robert
Robert

Reputation: 8663

$art_entities->{$rel_art_path}++;

$art_entities is a variable (a scalar, see the leading $ sign). With the arrow syntax, it looks like it is an object.

$rel_art_path is another scalar variable.

->{...} accesses a field (right side of the arrow, in {curly braces}) from the object on the left side of the arrow. Perl does not do much in terms of access limitations, i.e., there is no public, private, or protected. You could add your own code to enforce private access (see Perl Best Practices) or use a class framework like Moose.

The code uses the value of $rel_art_path as a field name in the $art_entities object.

In Java, this would be done with reflection:

import java.lang.reflect.Field;

public class SomeClass {
    public static void main(String[] args) throws Exception {
        SomeOtherClass soc = new SomeOtherClass();
        doSomething(soc, "someValue");
        soc.print();
    }

    public static void doSomething(Object art_entities, String rel_art_path)
    throws Exception {
        Class<?> c = art_entities.getClass();
        Field field = c.getDeclaredField(rel_art_path);
        field.setInt(art_entities, field.getInt(art_entities) + 1);
    }
}

class SomeOtherClass {
    public int someValue = 0;

    public void print() {
        System.out.println("my value is " + someValue);
    }
}

(I've used your variable names in the Java code to make it clearer which corresponds to which. Obviously they are Perl style (using underscores), not Java (camel case).)

In Java, this is hackish and quite a lot to write, in Perl it's short and normal.

Upvotes: -1

Related Questions