Ratazong has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,

I am participating in a coding-contest, where you have to steer a bot through an arena and collect gems. (hidden gems (german only)). I am one of only two contestants using perl.

As part of the rules the bot must behave deterministic - two runs in the same arena need to result in the same result. For random numbers this is ensured by setting a fixed seed in srand().

The challenge comes with hashes, which by design are unordered. Here I ensure determinism using sort on the keys. But now I want to access the hash based on the order of a field inside:

foreach my $k sort { $hash{$a}{value} <=> $hash{$b}{value} } (keys %ha +sh)
- and the values are not unique. This line resulted in non-determinism (and therefore my bot was excluded from tonights competition).

Is there a way to force perl to access a hash in a deterministic way? Similar to srand()? Or what is the preferred way to enforce it? I could try something like

foreach my $k sort { $hash{$a}{value} <=> $hash{$b}{value} } (sort key +s %hash)
but that doesn't seem to be elegant to me ...

Rata (waiting for enlightment)

Replies are listed 'Best First'.
Re: Perl hashes and non-determinism
by hippo (Archbishop) on Nov 19, 2025 at 10:11 UTC

    The values might not be unique but the keys certainly are. Sort by value first and then by key - that will give you a deterministic order. This is essentially what you have aimed for in your "inelegant" solution. The downside there is that you are sorting all the keys when it is only those with identical values which actually need it. I'd prefer to use something like

    foreach my $k sort { $hash{$a}{value} <=> $hash{$b}{value} || $a cmp $b} (keys %hash)

    Does this seem more elegant to you?


    🦛

Re: Perl hashes and non-determinism
by ikegami (Patriarch) on Nov 19, 2025 at 18:53 UTC

    Similar to srand()?

    Env var PERL_HASH_SEED would be the equivalent. Setting it will use the provided value as a seed.

    $ demo() { perl -e' use v5.18; my %h; ++$h{ $_ } for "a".."z"; say keys %h; ' } $ demo nylxbvceamuszdrotqfgpwikhj $ demo ahedybntzwgmpulqijkrsxfvoc $ PERL_HASH_SEED=0 demo udbaxqhcnmskzeiwryvlftojgp $ PERL_HASH_SEED=0 demo udbaxqhcnmskzeiwryvlftojgp $ PERL_HASH_SEED=0x0123456789ABCDEf demo lfojtgpzekwriyvchmnsuadbqx $ PERL_HASH_SEED=0x0123456789ABCDEF demo lfojtgpzekwriyvchmnsuadbqx

    Even with PERL_HASH_SEED=0 (which is documented to remove the random perturbations), the order is obscure, and the order of the keys can change as you add new elements. But the order and changes to it will be the same every run (as long as the program is run on similar-enough builds of Perl).

    Note that there are denial-of-service risks associated with removing the random perturbations when the keys are controlled by an external actor. This probably doesn't matter for your contest.

    The env var needs to be set before perl starts, but you can use bootstrapping to achieve this if you can't control that aspect of the contest's environment.

    BEGIN { return if exists( $ENV{ PERL_HASH_SEED } ); $ENV{ PERL_HASH_SEED } = "0"; exec( $^X, "--", $0, @ARGV ) or die( "exec: $!" ); }

      Thanks for all your replies and the many suggestions you gave, dear monks! I will keep them in mind for my next refactoring of the code.

      All the best, Rata

Re: Perl hashes and non-determinism
by ikegami (Patriarch) on Nov 19, 2025 at 19:07 UTC

    You can use something that looks like a hash, but provides ordered results: Tie::IxHash

    $ demo() { perl -e' use v5.18; use Tie::IxHash qw( ); my %h; tie %h, Tie::IxHash:: if $ARGV[0]; ++$h{ $_ } for "a".."z"; say keys %h; ' -- "$1" } $ demo 0 lukphmweqsiorxvnbdfajyczgt $ demo 0 dpbwuxlvticosyngmrzajefqkh $ demo 1 abcdefghijklmnopqrstuvwxyz $ demo 1 abcdefghijklmnopqrstuvwxyz

    You'd have to tie not just %hash, but the hashes referenced by the values of %hash.

Re: Perl hashes and non-determinism
by sleet (Pilgrim) on Nov 19, 2025 at 19:00 UTC
Re: Perl hashes and non-determinism
by ikegami (Patriarch) on Nov 19, 2025 at 19:15 UTC

    You can use a third-party dictionary or associative array implementation that provides deterministic orderings, such as Hash::Ordered.

    $ demo() { perl -e' use v5.18; use Hash::Ordered qw( ); my $h = Hash::Ordered->new(); $h->set( $_, 1 ) for "a".."z"; say $h->keys; ' } $ demo abcdefghijklmnopqrstuvwxyz $ demo abcdefghijklmnopqrstuvwxyz

    You'd have to replace not just %hash, but the hashes referenced by the values of %hash.

Re: Perl hashes and non-determinism
by cavac (Prior) on Nov 19, 2025 at 09:12 UTC