Я хочу сортировать массив массивов в 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;
Ответы
Ответ 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, если левая часть численно меньше, чем, равный или больший, чем правая часть соответственно. ||
имеет более высокий приоритет, поэтому перед возвратом вычисляется все булево выражение. Как уже упоминалось, вы можете заключить выражение в круглые скобки, если вы предпочитаете это значение ||
. Я лично этого не делаю.