在perl中的三列文本文件中应用距离公式

use*_*209 0 perl distance text-files

这是我的数据

nodes             x             y         z

1112,         23,       56,       88
2223,         56,       78,       54
3345,         32,       12,       11
4321,         11,       10,       06
5234,         10,       10,       12
6123,         12,       06,       04
Run Code Online (Sandbox Code Playgroud)

现在我想要那些彼此最近的节点.例如:如果节点(1112)和节点(4321)之间的距离小于(令d = 10)节点(1112)和节点(3345)之间的距离(让d = 34)反之亦然,输出应为=

     node:1112 and node:4321 are closest
     node:6123 and node:3345 are closest.....
Run Code Online (Sandbox Code Playgroud)

我正在使用这样的距离公式:my $ dist = sqrt(($ x- $ x2)*2 +($ y- $ y2)*2 +($ z- $ z2)**2);

 i.e:
 d=sqrt( (56-23)**2 + (78-56)**2 + (54-88)**2 )
 d=sqrt( (32-23)**2 + (12-56)**2 + (11-88)**2 )
 d=sqrt( (11-23)**2 + (10-56)**2 + (06-88)**2 )
 d=sqrt( (10-23)**2 + (10-56)**2 + (12-88)**2 )....and so on
Run Code Online (Sandbox Code Playgroud)

类似地,对于其他节点及其坐标

 d=sqrt( (23-56)**2 + (56-78)**2 + (88-54)**2 )
 d=sqrt( (32-56)**2 + (12-78)**2 + (11-54)**2 )
 d=sqrt( (11-56)**2 + (10-78)**2 + (06-54)**2 )
 d=sqrt( (10-56)**2 + (10-78)**2 + (12-54)**2 )....
Run Code Online (Sandbox Code Playgroud)

我做到了这一点却什么都没有......

use strict;
use warnings;
use Data::Dumper;

open(IN , "<" , "1.txt");
#open(OUT , ">" , "out.txt");

my $node_flag=0;
my %node_hash_1;
my %node_hash_2;

my @node_1;
my @node_2;

my @distance_array;
my $i=0;

foreach my $line(<IN>)
{
    if($line=~/^\*node$/i)
    {
        $node_flag=1;
        next;
    }
    if($node_flag==1)
    {
        if($line!~/^\*\*/i)
        {
            my @array=split(",",$line); # or  my @array=(split(",",$line))[1,2,3]
            my $node_id=shift @array;
            $node_hash_1{$node_id}=\@array;

            push(@node_1,[@array]);
            push(@node_2,[@array]);

            foreach my $var1(@node_1)
            {
                my($x,$y,$z)=@{$var1};
                foreach my $var2(@node_2)
                {
                    my($x2,$y2,$z2)=@{$var2};
                    my $dist = sqrt( ($x-$x2)**2 + ($y-$y2)**2 + ($z-$z2)**2 );
#                   print $dist;
                    print"\n";
                    foreach my $value(@{$node_hash_1{$node_id}})
                    {
                        my $vars=join",",@{$node_hash_1{$node_id}};
                        my @arr=split ",",$vars;
#                       print $arr[0];
                        print $vars;
                        if ($x==$value && $y==$value && $z==$value)
                        {
                            push @{$node_hash_1{$node_id}},$dist;
#                           $node_hash_2{x} = $x;
#                           $node_hash_2{y} = $y;
#                           $node_hash_2{z} = $z;
                        }
                    }
                }
            }
        }
        else
        {
            $node_flag=0;
        }
    }
}
#print Dumper(\%node_hash);
#print Dumper(\@distance_array);
#print Dumper(\@node_1);
#print Dumper(\%node_hash_2);
Run Code Online (Sandbox Code Playgroud)

Axe*_*man 5

输入行永远不会匹配'*node'所以$node_flag永远不会被设置,所以第二个if被完全跳过,跳过一切.

  • 由于'nodes'a 's'和来自第一行的开头,我不认为你有正确的.
  • 此外,由于我在输入中看不到任何文字 '*',我认为你不明白这/\*/是一种匹配文字明星的方式.

我修改了正则表达式来读取m/^nodes\b/,至少设置$ node_flag并让我进入主体.这是事情,但如果您知道文件顶部会出现一定数量的行,并且您不需要这些信息,则可以:

<IN> foreach 1..2; 
# or just once if that blank line is just a formatting mistake.
Run Code Online (Sandbox Code Playgroud)

如果要检查标题行:

die 'No Header Line!' unless <IN> =~ m/^nodes\b/;
Run Code Online (Sandbox Code Playgroud)

这样,你不进入循环(while无论如何它应该是一个循环).

  • 您将相同的坐标推入两个数组,表明您过度使用了foreach循环.

  • 此外,做一个笛卡尔比较收集的数据可以以更好的方式来完成,如果你保持当前的输入值淘汰之列,比较它已经存储在列表中的成员.看起来您最终会将节点#1与节点#1进行比较,因为您拥有的线路数量很多.

虽然如果我真的关心数据,我可能会将它分解为一组类,但我将以下更改作为直接perl行处理器提供:

my ( @node_list, %distance_between, %closest_to );

die 'No Header' unless <IN> =~ m/^nodes\b/;

while ( my $line = <IN> ) {
    my ( $name, $x, $y, $z ) = split /\s*,\s*/, $line;
    foreach my $comp_node ( @node_list ) {
        my ( $name2, $x2, $y2, $z2 ) = @$comp_node;

        # distance_between is a needless structure, if 
        # you don't want to keep the distance. 
        $distance_between{ $name2 }{ $name } 
            = $distance_between{ $name }{ $name2 } 
            = my $dist
            = sqrt( ($x-$x2)**2 + ($y-$y2)**2 + ($z-$z2)**2 )
            ;

        my $closest = $closest_to{ $name2 } ||= [ $name, $dist ];
        if ( $closest->[1] > $dist ) { 
            $closest_to{ $name2 } = [ $name, $dist ];
        }
        $closest = $closest_to{ $name } ||= [ $name2, $dist ];
        if ( $closest->[1] > $dist ) { 
            $closest_to{ $name } = [ $name2, $dist ];
        }
    }
    push @node_list, [ $name, $x, $y, $z ];
}
foreach my $p ( map { [ $_, $closest_to{ $_ }[0] ] } sort keys %closest_to ) {
    say "$p->[0]'s closest neighbor is $p->[1]" ;
}
Run Code Online (Sandbox Code Playgroud)