perl: перемешать отсортированный по значению хэш? - PullRequest
2 голосов
/ 29 ноября 2011

Сначала извините за мой английский - надеюсь, вы меня поймете.

Есть хэш:

$hash{a} = 1;
$hash{b} = 3;
$hash{c} = 3;
$hash{d} = 2;
$hash{e} = 1;
$hash{f} = 1;

Я хочу отсортировать его по значениям (не ключам), поэтому яиметь:

for my $key ( sort { $hash{ $a } <=> $hash{ $b } } keys %hash  ) { ... }

И сначала я получаю все ключи со значением 1, затем со значением 2 и т. д. Отлично.

Но если хэш не меняется, порядокключи (в этой сортировке по значению) всегда одинаковы.

Вопрос: Как я могу перемешать результаты сортировки, поэтому каждый раз, когда я запускаю цикл for, я получаю различный порядок ключей со значением 1, значение 2 и т. д.?

Ответы [ 5 ]

4 голосов
/ 29 ноября 2011

Не совсем уверен, что я хорошо понимаю ваши потребности, но это нормально:

use List::Util qw(shuffle);

my %hash;
$hash{a} = 1;
$hash{b} = 3;
$hash{c} = 3;
$hash{d} = 2;
$hash{e} = 1;
$hash{f} = 1;

for my $key (sort { $hash{ $a } <=> $hash{ $b } } shuffle( keys %hash  )) {
    say "hash{$key} = $hash{$key}"
}
3 голосов
/ 29 ноября 2011

Вы можете просто добавить другой уровень сортировки, который будет использоваться, когда обычный метод сортировки не может различить два значения. E.g.:

sort { METHOD_1 || METHOD_2 || ... METHOD_N } LIST

Например:

sub regular_sort {
    my $hash = shift;
    for (sort { $hash->{$a} <=> $hash->{$b} } keys %$hash) {
        print "$_ ";
    };
}
sub random_sort {
    my $hash = shift;
    my %rand = map { $_ => rand } keys %hash;
    for (sort { $hash->{$a} <=> $hash->{$b} ||
        $rand{$a} <=> $rand{$b} } keys %$hash ) {
        print "$_ ";
    };
}
2 голосов
/ 29 ноября 2011

Чтобы отсортировать ключи по значению при случайном порядке ключей с одинаковыми значениями, я вижу два решения:

use List::Util qw( shuffle );
use sort 'stable';
my @keys =
   sort { $hash{$a} <=> $hash{$b} }
   shuffle keys %hash;

или

my @keys =
   map $_->[0],
   sort { $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] }
   map [ $_, $hash{$_}, rand ],
   keys %hash;

use sort 'stable'; требуется для предотвращения sort искажения случайности списка, возвращаемого shuffle.


Вышеупомянутое преобразование Шварца не является попыткой оптимизации. Я видел, как люди использовали rand в самой функции сравнения, чтобы попытаться достичь вышеуказанного результата, но это приводит к ошибкам по двум причинам.

При использовании «неправильного» сравнения, такого как результат, результаты документируются как undefined , поэтому sort разрешено возвращать мусор, повторяющиеся элементы, отсутствующие элементы и т. Д.

Даже если sort не вернет мусор, это будет нечестно. Результат будет взвешен.

1 голос
/ 29 ноября 2011

Похоже, вы хотите, чтобы циклически перебирать клавиши.

Perl, не хранит в последовательном или отсортированном порядке, но это не кажется достаточно случайным для вас, поэтому вы можете захотеть создать массив ключей и циклически пройти по нему.

Сначала заполните массив ключами, затем используйте алгоритм случайных чисел (1 .. $ # length_of_array), чтобы нажать клавишу в этой позиции массива, в array_of_keys.


Если выВы пытаетесь рандомизировать ключи хэша, отсортированного по значению, это немного отличается.

См. Кодовую панель

my %hash = (a=>1, b=>3, c=>3, d=>2, e=>1, f=>1);
my %hash_by_val;

for my $key ( sort { $hash{$a} <=> $hash{$b} } keys %hash ) { 
   push @{ $hash_by_val{$hash{$key}} }, $key;
}


for my $key (sort keys %hash_by_val){
   my @arr        = @{$hash_by_val{$key}};
   my $arr_ubound = $#arr;

   for (0..$arr_ubound){
      my $randnum = int(rand($arr_ubound));
      my $val     = splice(@arr,$randnum,1);
      $arr_ubound--;
      print "$key : $val\n";                    # notice: output varies b/t runs
   }
}
1 голос
/ 29 ноября 2011

Вы можете иметь две функции для возрастания и убывания и использовать их соответственно как

sub hasAscending {
   $hash{$a} <=> $hash{$b};
}

sub hashDescending {
   $hash{$b} <=> $hash{$a};
}

foreach $key (sort hashAscending (keys(%hash))) {
   print "\t$hash{$key} \t\t $key\n";
}

foreach $key (sort hashDescending (keys(%hash))) {
   print "\t$hash{$key} \t\t $key\n";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...