在perl数组中重新排序对

时间:2011-08-23 09:44:36

标签: arrays perl

我从另一个程序向perl程序提供了一系列二维坐标。其中有4个,它们组成一个四边形,共计8个数字,例如:

x1 y1 x2 y2 x3 y3 x4 y4

我想确保它们都以相同的顺序指定,即顺时针或逆时针。我已经知道如何做到这一点,并且通过查看交叉产品的标志来做到这一点。

use strict;
use warnings;

my $line = "-0.702083 0.31 -0.676042 -0.323333 0.74375 -0.21 0.695833 0.485";
my @coord = split(/[,\s]+/, $line);

# Vector cross product (Z is 0) to test CW/CCW
my @v1 = (-$coord[2]+$coord[0], -$coord[3]+$coord[1]);
my @v2 = (-$coord[2]+$coord[4], -$coord[3]+$coord[5]);
my $cross = ($v1[0]*$v2[1]) - ($v1[1]*$v2[0]);

一旦我确定需要更改订单,我目前使用以下方式进行更改:

@coord = ($coord[6], $coord[7], $coord[4], $coord[5], 
          $coord[2], $coord[3], $coord[0], $coord[1]) if ($cross < 0);

这很有效,但我很确定这不是用perl编写它的最好方法。是否有一种更优雅,“更好”的方式来按顺序编写这种变化?最适合$n 2-D对的东西。这并不是简单地逆转数组问题的元素。

4 个答案:

答案 0 :(得分:8)

可以使用数组切片重写最后几行:

@coord = @coord[6,7,4,5,2,3,0,1] if $cross < 0;

要处理任意数量的对,您可以使用List::MoreUtils::natatime

use List::MoreUtils 'natatime';   

my $it = natatime 2, @coord;
@coord = (); 

while (my @vals = $it->()) {
    unshift @coord, @vals;
}

答案 1 :(得分:2)

对于$n 2-D对,您需要一个返回新订单列表的函数。
例如$n == 8

sub reorder {
    my $n = shift;
    return (6,7,4,5,2,3,0,1) if $n == 8;
}

然后你可以在数组切片中使用它:

$n = 8;
@coord = @coord[reorder($n)] if $cross < 0;

答案 2 :(得分:2)

我最近遇到了类似的问题,并采用了这种简洁的算法:

splice @coords, $_, 2, [ $coords[$_],$coords[$_+1] ]  for 0..$#coords/2;
@coords = map { @$_ } reverse @coords;

第一行将平面列表转换为坐标对列表,例如, (0,1,10,11,50,51) ==> ( [0,1], [10,11], [50,51] )

第二行反转对的顺序并再次展平列表。

<小时/>

更新:更简洁:

@coords = @coords[reverse map{$_ ^ 1}0..$#coords] if $cross < 0;

@coords = @coords[map {-$_ ^ 1} 1..@coords] if $cross < 0;

@coords = @coords[map {$_ ^ -1} 1..@coords] if $cross < 0;

答案 3 :(得分:0)

以eugene的答案为基础。构建反向列表的方法:

my $i = 0; 
unshift @i, $i++, $i++ while ($i <= $#coord); 
@coord = @coord[@i];