Perl:do-while 遍历 "growing" 数组散列中的键数产生 "extra" 输出

Perl: do-while loop over number of keys in a "growing" Hash of Arrays produces "extra" output

我不确定我是否在标题中说得很清楚,但我的问题是这样的:我有一个 do-while 循环,它接受一个初始化的数组散列 (HoA),计算一个新的维度数组,然后将生成的数组与与每个键的数组关联的数组进行比较(为简单起见,键是数字的……所以最好将其作为数组的数组来执行)。如果某些 crietria 是合适的(例如,新值在特定 "distance" 内),则会为 HoA 生成一个新密钥,并将生成的数组与新密钥一起添加到 HoA。

奇怪的是我用 do-while 循环限制了新键的数量,但有时当我 运行 代码(根本没有做任何更改)时,我得到 "extra" 键。

我的代码在下面,任何帮助都会很棒。

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

my $PolymerSize=6;

# each monomer/node can bond upto 3 times
my $maxNeighbors=3;

#Store coordinates and number of neighbors for each  monomer/node
# (in C would be an array of structs)

my %HoA;
my %BondHoA; #who are my neighbors
my @coordsNeighbors;
my $element; #Iteration dummy variable
my %dist; #temporary distance hash
my $return_flag;

$coordsNeighbors[0]=0; #Xcoord
$coordsNeighbors[1]=0; #YCoord
$coordsNeighbors[2]=0; #ZCoord
$coordsNeighbors[3]=0; #How many bonded neighbors?

#Intialize origin (first node/monomer)
push @{$HoA{0}}, $coordsNeighbors[0];
push @{$HoA{0}}, $coordsNeighbors[1];
push @{$HoA{0}}, $coordsNeighbors[2];
push @{$HoA{0}}, $coordsNeighbors[3];

#Generate new nodes/monomer and "grow" polymer
do{
    for(my $j=0;$j<3;$j++){
        #generate coords of potent. monomers/node
        $coordsNeighbors[$j] = int($PolymerSize*rand());
    }

    $coordsNeighbors[3]=0;

    #loop through existing monomers/nodes
    foreach $element ( keys %HoA) {
        #if this monomer doesn't have the max bonds proceed
        if( ($HoA{$element}[3])!=$maxNeighbors) {
            my $tempx=$HoA{$element}[0]-$coordsNeighbors[0];
            my $tempy=$HoA{$element}[1]-$coordsNeighbors[1];
            my $tempz=$HoA{$element}[2]-$coordsNeighbors[2];

            #make hash of L1 distances
            $dist{$element} .=abs($tempx)+abs($tempy)+abs($tempz);
        }
    }

    #check if any distance is != 1; no-bond could be made if so
    foreach(keys %dist){
        if($dist{$_}!=1) {
            delete $dist{$_};
        }
    }

    #potential monomer is good, so add to HoA and update bonds
    foreach $element (keys %dist){
        $HoA{$element}[3]++;
        my $newKey=scalar (keys %HoA);
        if($newKey!=($PolymerSize-1)){
            push @{$HoA{$newKey}}, $coordsNeighbors[0];
            push @{$HoA{$newKey}}, $coordsNeighbors[1];
            push @{$HoA{$newKey}}, $coordsNeighbors[2];
            push @{$HoA{$newKey}}, $coordsNeighbors[3]+1;
            push @{$BondHoA{$element}}, "$newKey";
            push @{$BondHoA{$newKey}}, "$element";
        }
        delete $dist{$element};
    }

} while((keys %HoA)<$PolymerSize-1);

foreach $element (keys %HoA) {
    print "$element \t $HoA{$element}[0] \t $HoA{$element}[1] \t $HoA{$element}[2]\n";
}

此代码背后的总体思路是做一些事情,比如在 3D 中像 DLA(diffusion-limited 聚合体)一样生长聚合物,因此需要满足两点才能使其正常工作:

1) 获得正确数量的单体(上面提到的 HoA 密钥)。

2) 确保没有 monomer-overlaps(没有 L1 距离为 0(曼哈顿距离,因为我们在网格上))。

[编辑] 我显然忘了包括所需的输出(我很抱歉)。

输出应该是这样的:

0   0  0  0
1   1  0  0
2   0  1  0
3   2  0  0
4   2  1  0
5   2  2  0

但我最终得到的是:

0   0   0  0
1   1   0  0
2   0   1  0
3   0   0  0 
4   1   1  0
5   0   1  1
6   2   0  0

(有时甚至是第 8 或第 9 个值)

简单修改即可

确保哈希大小为 6

        my $newKey=scalar (keys %HoA);
        print "newKey<$newKey>\n";
        if($newKey !=($PolymerSize-1)){
        # changed to
        if($newKey <=($PolymerSize-1)){

确保散列的大小为 6

} while((keys %HoA)<$PolymerSize-1);
#change to
} while((keys %HoA)<=$PolymerSize-1);

按照@Schwern 的建议,我回去查看了

foreach $element (keys %dist){ ...

循环并意识到实际上发生的事情是,即使在删除所有与 %dist 散列中的 non-one 值一致的键之后,我在 %dist 散列中可能有超过 1 个成员。然后一个简单的修复是添加一个 "last"

foreach $element (keys %dist){
    $HoA{$element}[3]++;
    my $newKey=scalar (keys %HoA);
    if($newKey!=($PolymerSize-1)){
        push @{$HoA{$newKey}}, $coordsNeighbors[0];
        push @{$HoA{$newKey}}, $coordsNeighbors[1];
        push @{$HoA{$newKey}}, $coordsNeighbors[2];
        push @{$HoA{$newKey}}, $coordsNeighbors[3]+1;
        push @{$BondHoA{$element}}, "$newKey";
        push @{$BondHoA{$newKey}}, "$element";
       #here I add a last to break out and go to the next do-while iter.
        last
    }

然后在 do-while 循环的顶部添加对 undef(%dist) 的调用,以清除它以供再次使用。

此外,在修复此错误后,我返回 cleaned-up 大部分代码,现在在下面为感兴趣的人提供:

#!/usr/bin/perl;
use strict;
use warnings;
use Data::Dumper;
my $PolymerSize=8;
my $maxNeighbors=3; #each monomer/node can bond upto 3 times
my %HoA; #Store coordinates and number of neighbors for each monomer/node (in C would be an array of structs)
my %BondHoA; #who are my neighbors
my @coordsNeighbors;
my $element; #Iteration dummy variable
my %dist; #temporary distance hash
my $selected;
$coordsNeighbors[0]=0; #Xcoord
$coordsNeighbors[1]=0; #YCoord
$coordsNeighbors[2]=0; #ZCoord
$coordsNeighbors[3]=$maxNeighbors; #How many bonded neighbors?
my $tempx;
my $tempy;
my $tempz;
my $tempL1;
#Intialize origin (first node/monomer)
push @{$HoA{0}}, $coordsNeighbors[0];
push @{$HoA{0}}, $coordsNeighbors[1];
push @{$HoA{0}}, $coordsNeighbors[2];
push @{$HoA{0}}, $coordsNeighbors[3];
my $iter=0;
#Generate new nodes/monomer and "grow" polymer
do{
 #print "$iter\n";
 # $iter++;
  undef(%dist);
        for(my $j=0;$j<3;$j++){
             $coordsNeighbors[$j]=int($PolymerSize*rand()); #generate coords of potent. monomers/node
         }
             $coordsNeighbors[3]=$maxNeighbors;
        foreach $element ( keys %HoA) { #loop through existing monomers/nodes
                $tempx=int($HoA{$element}[0])-int($coordsNeighbors[0]);
                $tempy=int($HoA{$element}[1])-int($coordsNeighbors[1]);
                $tempz=int($HoA{$element}[2])-int($coordsNeighbors[2]);
                $tempL1=abs($tempx)+abs($tempy)+abs($tempz);
                  if($tempL1==1){
                        $dist{$element} .= $tempL1;
                  } elsif($tempL1==0){
                        undef(%dist);
                        last;
                  }
         }
        foreach $element (keys %dist){
                 if ( ($HoA{$element}[3]>0)){ #potential monomer is good, so add to HoA and update bonds
                        my $newKey=scalar (keys %HoA);
                        if($newKey<=($PolymerSize-1)){
                         $HoA{$element}[3]--;
                         push @{$HoA{$newKey}}, $coordsNeighbors[0];
                         push @{$HoA{$newKey}}, $coordsNeighbors[1];
                         push @{$HoA{$newKey}}, $coordsNeighbors[2];
                         push @{$HoA{$newKey}}, $coordsNeighbors[3];
                         push @{$BondHoA{$element}}, "$newKey";
                         push @{$BondHoA{$newKey}}, "$element";
                         last;
                        }
                 }
              }
} while((scalar (keys %HoA))<=$PolymerSize-1);
foreach $element (keys %HoA){
        print "$element \t $HoA{$element}[0] \t $HoA{$element}[1] \t $HoA{$element}[2]\n";
}