gpt4 book ai didi

perl - 嵌套while循环以计算多个目的地的距离

转载 作者:行者123 更新时间:2023-12-04 19:14:19 27 4
gpt4 key购买 nike

好吧,我现在搞砸了。我想使用严格和警告来创建一个合适的脚本(对我来说仍然是一个挑战;)。但现在我完全迷失了。我一直在看太多的例子,我完全糊涂了。我正在尝试使用纬度/经度计算两点之间的距离。我想我已经用 gis::distance 覆盖了那部分。但问题是我试图找到彼此相距 5000m 以内的目的地。 (如果目的地相同则跳过)。因此,当它找到距离另一个目的地 5000 米以内的目的地时,我希望它把它放在第一个文件中的最后一个元素之后。

两个输入文件是相同的,以下是它们的外观。这两个文件都有大约 45k 行。

Europe;3;France;23;Parijs;42545;48,856555;2,350976
Europe;3;France;23;Parisot;84459;44,264381;1,857827
Europe;3;France;23;Parlan;11337;44,828976;2,172435
Europe;3;France;23;Parnac;35670;46,4533;1,4425
Europe;3;France;23;Parnans;22065;45,1097;5,1456

假设这些目的地中有 2 个彼此靠近,我正在尝试像这样输出它:
Europe;3;France;23;Parijs;42545;48,856555;2,350976;Parlan;11337;200
Europe;3;France;23;Parisot;84459;44,264381;1,857827;
Europe;3;France;23;Parlan;11337;44,828976;2,172435;
Europe;3;France;23;Parnac;35670;46,4533;1,4425;Parisot;84459;2000;Parnans;22065;350
Europe;3;France;23;Parnans;22065;45,1097;5,1456;

实际结果当然会匹配2个以上。在输出文件中添加了匹配的目的地、目的地 id 和计算的距离。每个目的地可能有多个匹配项。噗这真的很难解释哈哈。正如我所说,我正在使用严格和警告并将错误缩小到最低限度,但仍然不完全。这些是错误:
Global symbol "$infile1" requires explicit package name at E:\etc.pl line 17.
Execution of E:\etc.pl aborted due to compilation errors.

这是我到目前为止的代码。我的声明也没有正面或反面。

有人能帮我吗? (也许这不是最有效的方法,但现在它可以帮助我逐步了解 perl)
use strict;
use warnings;
use GIS::Distance::Lite qw(distance);

my $inputfile1 = shift || die "Give input!\n";
my $inputfile2 = shift || die "Give more input!\n";
my $outputfile = shift || die "Give output!\n";

open my $INFILE1, '<', $inputfile1 or die "In use/Not found :$!\n";
open my $INFILE2, '<', $inputfile2 or die "In use/Not found :$!\n";
open my $OUTFILE, '>', $outputfile or die "In use/Not found :$!\n";

my $maxdist = 5000;
my $mindist = 0.0001;

while ( my @infile1 ){
my @elements = split(";",$infile1);

my $lat1 = $elements[6];
my $lon1 = $elements[7];

$lat1 =~ s/,/./g;
$lon1 =~ s/,/./g;

seek my $infile2, 0, 0;

print "1. $lat1\n";
print "2. $lon1\n";

while ( my @infile2 ){
my @loopelements = split(";",$infile2);

my $lat2 = $loopelements[6];
my $lon2 = $loopelements[7];

$lat2 =~ s/,/./g;
$lon2 =~ s/,/./g;

print "3. $lat1\n";
print "4. $lon1\n";

my $distance = distance($lat1, $lon1 => $lat2, $lon2); # Afstand berekenen tussen latlon1 and latlon2

print "5. $distance\n";

my $afstand = sprintf("%.4f",$distance);

print "6. $afstand\n";

if (($afstand < $maxdist) and (!($elements[4] == $loopelements[4]))){
push (@elements, $afstand,$loopelements[4],$loopelements[5]);
print "7. $afstand\n";
} else {
next;
}
}

@elements = join(";",@elements); # add ';' to all elements
print OUTFILE "@elements";
#if ($i == 10) {last;}
}
close(INFILE1);
close(INFILE2);
close(OUTFILE);

- - - - - - - - 编辑 - - - - - - -

嗯,我又来了。我一直在查看你更新的代码,这是我的一个非常激烈的版本哈哈。老实说,我只懂一半。它仍然很有帮助;全部!我决定通过您的改进坚持我的原始脚本设计,但它仍然无法正常工作。如果你不介意的话,我有几个问题:

我在脚本中做了一些调整。第一个是它现在跳过带有零的经纬度,因为这会产生无用的结果。在同一行中,它还会跳过同样无用的空单元格。我已经为两个 infile 做了这个。

哦,我说元素[4]的地方是元素[5],所以它是数字。所以我把 ne 换成了 != 如果我没记错的话。但我想我再次创建了一个无限循环,因为它没有遍历第二个文件。我知道我可能看起来很固执,但我想首先了解我的原始脚本,并在我运行后立即开始处理您的版本。我认为搜索功能无法正常工作。这是现在的脚本。
use strict;
use warnings;
use GIS::Distance::Lite qw(distance);

my $inputfile1 = shift || die "Give input!\n";
my $inputfile2 = shift || die "Give more input!\n";
my $outputfile = shift || die "Give output!\n";

open my $INFILE1, '<', $inputfile1 or die "In use/Not found :$!\n";
open my $INFILE2, '<', $inputfile2 or die "In use/Not found :$!\n";
open my $OUTFILE, '>', $outputfile or die "In use/Not found :$!\n";

my $maxdist = 3000;
my $mindist = 0.0001;

while (my $infile1 = <$INFILE1> ){
chomp $infile1;
my @elements = split(";",$infile1);

print "1. $elements[6]\n";
print "2. $elements[7]\n";

my $lat1 = $elements[6];
my $lon1 = $elements[7];

if ((($lat1 and $lon1) ne '0') and (!($lat1 and $lon1) eq "")){
$lat1 =~ s/,/./;
$lon1 =~ s/,/./;
print "lat1: $lat1\n";
print "lon1: $lon1\n";
} else {
next;
}

print "3. $lat1\n";
print "4. $lon1\n";

seek $INFILE2, 0, 0;

while ( my $infile2 = <$INFILE2> ){
chomp $infile2;
my @loopelements = split(";",$infile2);

print "5. $elements[6]\n";
print "6. $elements[7]\n";

my $lat2 = $loopelements[6];
my $lon2 = $loopelements[7];

if ((($lat2 and $lon2) ne '0') and (!($lat2 and $lon2) eq "")){
$lat2 =~ s/,/./;
$lon2 =~ s/,/./;
print "lat2: $lat1\n";
print "lon2: $lon1\n";
} else {
next;
}

my $distance = distance($lat1, $lon1 => $lat2, $lon2); # Afstand berekenen tussen latlon1 and latlon2

print "7. $distance\n";

my $afstand = sprintf("%.4f",$distance);

print "8. $afstand\n";

if ($afstand < $maxdist && $elements[4] != $loopelements[4]){
push (@elements, $afstand, $loopelements[4],$loopelements[5]);
print "9. $afstand\n";
} else {
next;
}
}
print $OUTFILE join(";",@elements), "\n";
}

close($INFILE1);
close($INFILE2);
close($OUTFILE);

最佳答案

你已经很不错了。让我们来看看您的错误消息。

Global symbol "$infile1" requires explicit package name at E:\etc.pl line 17.



这个很简单。在 Perl 中,所有 variable names 都区分大小写。在顶部,您创建一个 lexical 变量 $INFILE1 。稍后我将更多地讨论词法。
open my $INFILE1, '<', $inputfile1  or die "In use/Not found :$!\n";

在这里,您已将其全部大写,这没关系。如果它有助于您记住它是一个词法文件句柄(文件句柄曾经是全局的并且命名为 INFILE1 ),那么您可以这样做。 但是 稍后(第 17 行)您使用 $infile1
my @elements = split(";",$infile1);

您尚未声明该变量(使用 my ),因此它会引发此错误。但这还不是全部。

我相信您正在尝试从该文件句柄中读取。但这不起作用。我将分步解释这一点。
- 事实上,你已经建立了一个无限循环,但你还没有意识到它。
    while ( my @infile1 ){ 

这个 while 循环不会停止。曾经。 @infile1my 的声明总是返回一个真值,因为它总是有效的。所以你永远不会打破循环。
  • 我猜您是在尝试逐行读取文件。那么让我们看看如何做到这一点:
    while (my $infile1 = <$INFILE1> ){ 
    my @elements = split(";",$infile1);

    您需要像这样从文件中读取。现在 while 循环头中的赋值只有在有一行从文件句柄返回时才会为真。一旦到达文件末尾,它将返回 undef ,从而结束循环。好极了。还要注意下一行中的 $infile1split 现在是如何正确的。

    您还需要将 chomp 添加到组合中,因为文件末尾有换行符:
    while (my $infile1 = <$INFILE1> ){ 
    chomp $infile1;
    my @elements = split(";",$infile1);
  • 接下来是 seek 行。看起来您想从第一个文件的每一行的开头读取第二个文件。这在某种程度上是有道理的,但效率很低。我稍后再谈。不过,您确实需要更改 my。您不必在此处创建新变量。另外,使用正确的名称:
    seek $INFILE2, 0, 0;
  • 让我们来修复第二个 while 循环:
    while (my $infile2 = <$INFILE2>){
    chomp $infile2;
    my @loopelements = split(";",$infile2);
  • 我注意到的下一件事是在第 42 行:
    my $distance = distance($lat1, $lon1 => $lat2, $lon2);

    别担心,这里没有什么问题。我只想指出, => 是另一种写逗号 ( , ) 的方式。它有时被称为胖逗号,它使阅读更容易,例如,散列分配。
  • 在第 50 行中,您已经获得了距离。
    if (($afstand < $maxdist) and (!($elements[4] == $loopelements[4]))){     

    and 通常用于做错误检查。请参阅 perldoc 了解原因。您应该改用 && 。因为它具有更高的优先级,所以您可以省略括号。您还可以更改 !($a == $b) 构造以改用 != 运算符。 但是 因为它保存了城市名称,而且它是一个字符串而不是数字,所以您需要使用 ne ,它与 eq 相反。所以这一行现在变成:
    if ($afstand < $maxdist && $elements[4] ne $loopelements[4]){

    读起来好多了,不是吗?
  • 在第 58 行中,您对数组 join 进行 @elements 并将其分配给自身。这是比较奇怪的。它将用一个只有一个元素的新数组替换数组 - 连接字符串。让我们把那条线留到下一个项目符号,然后再看。
  • 在第 59 行中,您有一个 print 语句,但您现在使用的是从未创建的全局文件句柄 OUTFILE。相反,您需要使用顶部的词法文件句柄 $OUTFILE 。如果我们现在将上一行中的 join 直接添加到打印语句中,并在末尾添加一个 \n 换行符,该行将变为:
    print $OUTFILE join(";",@elements), "\n";
  • 现在只剩下最后一部分了:您需要关闭文件句柄,但您再次使用全局句柄。改用你的词汇:
    close($INFILE1);
    close($INFILE2);
    close($OUTFILE);

  • 完整的代码现在看起来像这样:
    use strict;
    use warnings;
    use GIS::Distance::Lite qw(distance);

    my $inputfile1 = shift || die "Give input!\n";
    my $inputfile2 = shift || die "Give more input!\n";
    my $outputfile = shift || die "Give output!\n";

    open my $INFILE1, '<', $inputfile1 or die "In use/Not found :$!\n";
    open my $INFILE2, '<', $inputfile2 or die "In use/Not found :$!\n";
    open my $OUTFILE, '>', $outputfile or die "In use/Not found :$!\n";

    my $maxdist = 5000;
    my $mindist = 0.0001;

    while (my $infile1 = <$INFILE1> ){
    chomp $infile1;
    my @elements = split(";",$infile1);

    my $lat1 = $elements[6];
    my $lon1 = $elements[7];

    $lat1 =~ s/,/./g;
    $lon1 =~ s/,/./g;

    print "1. $lat1\n";
    print "2. $lon1\n";

    seek $INFILE2, 0, 0;

    while ( my $infile2 = <$INFILE2> ){
    chomp $infile2;
    my @loopelements = split(";",$infile2);

    my $lat2 = $loopelements[6];
    my $lon2 = $loopelements[7];

    $lat2 =~ s/,/./g;
    $lon2 =~ s/,/./g;

    print "3. $lat1\n";
    print "4. $lon1\n";

    my $distance = distance($lat1, $lon1 => $lat2, $lon2); # Afstand berekenen tussen latlon1 and latlon2

    print "5. $distance\n";

    my $afstand = sprintf("%.4f",$distance);

    print "6. $afstand\n";

    if ($afstand < $maxdist && $elements[4] ne $loopelements[4]){
    push (@elements, $afstand,$loopelements[4],$loopelements[5]);
    print "7. $afstand\n";
    } else {
    next;
    }
    }

    print $OUTFILE join(";",@elements), "\n";
    }

    close($INFILE1);
    close($INFILE2);
    close($OUTFILE);

    现在谈谈你的算法的工作方式:首先读取完整的第二个文件,然后在每次迭代中与第一个文件进行比较会更有效率。这样,您只需读取一次文件。
    use strict;
    use warnings;
    use GIS::Distance::Lite qw(distance);
    use feature qw(say);

    my $inputfile1 = shift || die "first file missing";
    my $inputfile2 = shift || die "second file missing";
    my $outputfile = shift || die "output file missing!";

    # Read the second file first
    my @file2; # save the lines of INFILE2 as array refs
    open my $INFILE2, '<', $inputfile2 or die "In use/Not found :$!";
    while ( my $infile2 = <$INFILE2> ){
    chomp $infile2;
    my @loopelements = split(/;/, $infile2);

    $loopelements[6] =~ y/,/./;
    $loopelements[7] =~ y/,/./;

    push @file2, \@loopelements;
    }
    close($INFILE2);

    open my $INFILE1, '<', $inputfile1 or die "In use/Not found :$!";
    open my $OUTFILE, '>', $outputfile or die "In use/Not found :$!";

    my $maxdist = 5000;
    my $mindist = 0.0001;

    while (my $infile1 = <$INFILE1> ){
    chomp $infile1;
    my @elements = split(";",$infile1);

    my $lat1 = $elements[6];
    my $lon1 = $elements[7];

    $lat1 =~ y/,/./;
    $lon1 =~ y/,/./;

    say "1. $lat1";
    say "2. $lon1";

    FILE2: foreach my $loopelements ( @file2 ){
    my ($lat2, $lon2) = @$loopelements[6, 7];

    say "3. $lat2";
    say "4. $lon2";

    my $distance = distance($lat1, $lon1 => $lat2, $lon2); # Afstand berekenen tussen latlon1 and latlon2

    say "5. $distance";

    my $afstand = sprintf("%.4f",$distance);

    say "6. $afstand";

    if ($afstand < $maxdist && $elements[4] ne $$loopelements[4]){
    push (@elements, $afstand, $$loopelements[4], $$loopelements[5]);
    say "7. $afstand";
    } else {
    next FILE2;
    }
    }

    say $OUTFILE join(";",@elements);
    }

    close($INFILE1);
    close($OUTFILE);

    现在,让我们看看我改变了什么。
  • 首先,我在顶部添加了use feature qw(say) say print 相同,但添加了一个新行。这样可以节省一些打字时间。另请参阅 feature 了解更多信息。
  • 我从所有 die 语句中删除了“\n”字符。如果你在那里放一个新行,它会从输出中删除行号。如果这是故意的,请忽略此建议。以下是 Perldoc 对此的看法:

    If the last element of LIST does not end in a newline, the current script line number and input line number (if any) are also printed, and a newline is supplied.

  • 最重要的部分是我对算法的改变。我将第二个文件的 while 循环移到另一个 while 循环之外的程序顶部。该文件被放入数组 @file2 中。每个元素都包含一个包含行字段的数组 ref。逗号已更改为句号。

    我将 s/// 替换运算符更改为 y/// (缺少 tr/// )音译运算符。因为你只改变一个符号,这就足够了。它也更快。即使您保留正则表达式替换,您也不需要 /g 修饰符,因为浮点数只有一个逗号,因此不必进行多次替换。

    现在所有这些事情只对 file2 完成一次。当完成 40k+ 次时,这可以节省大量的计算时间。
  • 我更改了错误消息的措辞,以便更好地理解它们。那就是偏好。你不必那样做。
  • 我将第二个 while 更改为 foreach 循环,以迭代新的 @file2 数组的元素。为了清楚起见,我确实保留了 $lat2$lon2 变量。您可以省略这些,直接使用 array(ref) 元素。在作业中,我使用 array slice 将其放入一行。
  • 由于 $loopelements 替换了 @loopelements 并且它是一个数组引用,我们现在需要使用 $$loopelements[$index] 访问存储在其中的数据。

  • 我希望这可以帮助您理解为什么我进行了某些改进。

    请记住,在 Perl 中有不止一种方法可以做到这一点 - 这是一件好事。
    很少有正确的方法,但通常有很多方法可以通向目标。有些比其他的更有效,而其他的有时更容易维护。诀窍是在这两种情况之间找到正确的平衡。

    更新:

    这是我使用过的输入文件。您将需要它们来比较结果。

    文件1.csv:
    Europe;3;France;23;Parijs;42545;48,856555;2,350976
    Europe;3;France;23;Parisot;84459;44,264381;1,857827
    Europe;3;France;23;Parlan;11337;44,828976;2,172435
    Europe;3;France;23;Parnac;35670;46,4533;1,4425
    Europe;3;France;23;Parnans;22065;45,1097;5,1456

    file2.csv:
    Europe;3;France;23;Parlan;11337;44,828976;2,172435
    Europe;3;France;23;Parnac;35670;46,4533;1,4425
    Europe;3;France;23;Parnans;22065;45,1097;5,1456
    Europe;3;France;23;Parijs;42545;48,856555;2,350976
    Europe;3;France;23;Parisot;84459;44,264381;1,857827

    关于perl - 嵌套while循环以计算多个目的地的距离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11625649/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com