Подтвердить что ты не робот

Я хочу сортировать массив массивов в Perl, но результат не отсортирован

У меня есть массив массивов, которые я хочу сортировать. Каждый элемент массива A представляет собой массив с 3 элементами. Массив A выглядит так:

my @A = ([2,3,1], [1,2,3], [1,0,2], [3,1,2], [2,2,4]);

Я хочу сортировать A в порядке возрастания. При сравнении двух элементов используется первое число. Если есть галстук, используется второе число, а затем третье число.

Вот мой код. Я использую функцию 'cmpfunc' для сравнения двух элементов.

sub cmpfunc {
    return ($a->[0] <=> $b->[0]) or 
           ($a->[1] <=> $b->[1]) or
           ($a->[2] <=> $b->[2]);
}
my @B = sort cmpfunc @A;
print "Result:\n";
for my $element (@B) {
    print join(",", @{$element}) . "\n";
}

Результат:

1,2,3
1,0,2
2,3,1
2,2,4
3,1,2

Результат несколько отсортирован, но не корректен. Я ожидаю:

1,0,2
1,2,3
2,2,4
2,3,1
3,1,2

Есть ли ошибка в моей функции сравнения? Странная вещь: когда я помещаю код сравнения в блок, результат корректно сортируется.

my @C = sort { ($a->[0] <=> $b->[0]) or 
               ($a->[1] <=> $b->[1]) or
               ($a->[2] <=> $b->[2]) } @A;
4b9b3361

Ответ 1

Выполняется

return ($a->[0] <=> $b->[0])

который возвращается до того, как он попадет в любое из предложений "или".

Либо удалите ключевое слово "return", либо добавьте скобки вокруг всего списка arg для возврата:

sub cmpfunc {
    return(($a->[0] <=> $b->[0]) or
           ($a->[1] <=> $b->[1]) or
           ($a->[2] <=> $b->[2]));
}

Ответ 2

Причина, по которой вы наблюдаете это "неправильное" поведение, является приоритетом оператора or, максимально возможного. В этой ситуации это означает, что

return ($a->[0] <=> $b->[0]) or 
       ($a->[1] <=> $b->[1]) or
       ($a->[2] <=> $b->[2]);

интерпретируется как OR-ing

return ($a->[0] <=> $b->[0])

а остальная часть строки - глупость в этом случае, поскольку возврат никогда не возвращается.:)

Итак, вы должны использовать C OR:

return ($a->[0] <=> $b->[0]) || 
       ($a->[1] <=> $b->[1]) ||
       ($a->[2] <=> $b->[2]);

Ответ 3

Требуется больше скобок:

sub cmpfunc {
    return (($a->[0] <=> $b->[0]) or
            ($a->[1] <=> $b->[1]) or
            ($a->[2] <=> $b->[2]));
}

Ответ 4

    sub cmpfunc {
    return ($a->[0] <=> $b->[0]) or 
           ($a->[1] <=> $b->[1]) or
           ($a->[2] <=> $b->[2]);
}

здесь вы можете удалить 'return'.

    sub cmpfunc {
     ($a->[0] <=> $b->[0]) or 
     ($a->[1] <=> $b->[1]) or
     ($a->[2] <=> $b->[2]);
}

Ответ 5

Альтернативное решение для Дэниела:

sub cmpfunc {
    return ($a->[0] <=> $b->[0]) ||
           ($a->[1] <=> $b->[1]) ||
           ($a->[2] <=> $b->[2]);
}

Проблема с or заключается в том, что она имеет более низкий приоритет, чем присвоение, поэтому ваша функция возвращает результат ($a->[0] <=> $b->[0]), который равен -1, 0 или 1, если левая часть численно меньше, чем, равный или больший, чем правая часть соответственно. || имеет более высокий приоритет, поэтому перед возвратом вычисляется все булево выражение. Как уже упоминалось, вы можете заключить выражение в круглые скобки, если вы предпочитаете это значение ||. Я лично этого не делаю.