我有数据,看起来是这样的:

    my @homopol = (
                   ["T","C","CC","G"],  # part1
                   ["T","TT","C","G","A"], #part2
                   ["C","CCC","G"], #part3 ...upto part K=~50
                  );


    my @prob = ([1.00,0.63,0.002,1.00,0.83],
                [0.72,0.03,1.00, 0.85,1.00],
                [1.00,0.97,0.02]);


   # Note also that the dimension of @homopol is always exactly the same with @prob.
   # Although number of elements can differ from 'part' to 'part'.

什么我想要做的是

  1. 产生的所有组合的元素 part1 通过了 partK
  2. 找到的产品的相应的元素 @prob.

因此,在结束我们希望得到这个输出:

T-T-C  1 x 0.72 x 1 = 0.720
T-T-CCC     1 x 0.72 x 0.97 = 0.698
T-T-G  1 x 0.72 x 0.02 = 0.014
...
G-G-G  1 x 0.85 x 0.02 = 0.017
G-A-C  1 x 1 x 1 = 1.000
G-A-CCC     1 x 1 x 0.97 = 0.970
G-A-G  1 x 1 x 0.02 = 0.020

问题是,以下代码的地雷,并通过硬编码 循环。由于数量的部分 @homopol 是可以改变大 (例如-K=50),我们需要一个灵活的和紧凑的方式获得相同的结果。是否有任何?我想要使用 算::循环, 但不知道该如何实现这一目标。

use strict;
use Data::Dumper;
use Carp;


my @homopol = (["T","C","CC","G"],
               ["T","TT","C","G","A"],
               ["C","CCC","G"]);


my @prob = ([1.00,0.63,0.002,1.00,0.83],
            [0.72,0.03,1.00, 0.85,1.00],
            [1.00,0.97,0.02]);



my $i_of_part1 = -1;
foreach my $base_part1 ( @{ $homopol[0] } ) {
    $i_of_part1++;
    my $probpart1 = $prob[0]->[$i_of_part1];

    my $i_of_part2 =-1;
    foreach my $base_part2 ( @{ $homopol[1] } ) {
        $i_of_part2++;
        my $probpart2 = $prob[1]->[$i_of_part2];

        my $i_of_part3 = -1;
        foreach my $base_part3 ( @{ $homopol[2] } ) {
            $i_of_part3++;
            my $probpart3 = $prob[2]->[$i_of_part3];

            my $nstr = $base_part1."".$base_part2."".$base_part3;
            my $prob_prod = sprintf("%.3f",$probpart1 * $probpart2 *$probpart3);

            print "$base_part1-$base_part2-$base_part3 \t";
            print "$probpart1 x $probpart2 x $probpart3 = $prob_prod\n";

        }
    }
}
有帮助吗?

解决方案

我会推荐 Set::CrossProduct, ,这将创造一个迭代产生的交叉产品的所有集。因为它使用一个迭代,它不需要产生的每一个组合提前;相反,它产生每个人的需求。

use strict;
use warnings;
use Set::CrossProduct;

my @homopol = (
    [qw(T C CC G)],
    [qw(T TT C G A)],
    [qw(C CCC G)], 
);

my @prob = (
    [1.00,0.63,0.002,1.00],
    [0.72,0.03,1.00, 0.85,1.00],
    [1.00,0.97,0.02],
);

# Prepare by storing the data in a list of lists of pairs.
my @combined;
for my $i (0 .. $#homopol){
    push @combined, [];
    push @{$combined[-1]}, [$homopol[$i][$_], $prob[$i][$_]]
        for 0 .. @{$homopol[$i]} - 1;
};

my $iterator = Set::CrossProduct->new([ @combined ]);
while( my $tuple = $iterator->get ){
    my @h = map { $_->[0] } @$tuple;
    my @p = map { $_->[1] } @$tuple;
    my $product = 1;
    $product *= $_ for @p;
    print join('-', @h), ' ', join(' x ', @p), ' = ', $product, "\n";
}

其他提示

一个解决方案的使用 算::循环 没有改变输入的数据看起来是这样的:

use Algorithm::Loops;

# Turns ([a, b, c], [d, e], ...) into ([0, 1, 2], [0, 1], ...)
my @lists_of_indices = map { [ 0 .. @$_ ] } @homopol;

NestedLoops( [ @lists_of_indices ], sub {
  my @indices = @_;
  my $prob_prod = 1; # Multiplicative identity
  my @base_string;
  my @prob_string;
  for my $n (0 .. $#indices) {
    push @base_string, $hompol[$n][ $indices[$n] ];
    push @prob_string, sprintf("%.3f", $prob[$n][ $indices[$n] ]);
    $prob_prod *= $prob[$n][ $indices[$n] ];
  }
  print join "-", @base_string; print "\t";
  print join "x", @prob_string; print " = ";
  printf "%.3f\n", $prob_prod;
});

但我认为,你其实可以做的代码的更清晰的通过结构的改变为一个更喜欢

[ 
  { T => 1.00, C => 0.63, CC => 0.002, G => 0.83 },
  { T => 0.72, TT => 0.03, ... },
  ...
]

因为没有并行的数据结构可以简单地迭代过提供基本序列,而不是迭代指数然后查找这些指数在两个不同的地方。

为什么你不用递归?通过深入作为参数,并让的功能呼叫本身有深度+1内的循环。

你可以做的,它通过创建一系列的索引的同样的长度为@homopol阵列(N说),以跟踪它的组合,你看。事实上,这阵列是就像一个 数在基N,有的要素的数字。迭代以同样的方式为你会写下consectutive数字在基N,e。g(0 0 0...0),(0 0 0...1), ...,(0 0 0 ...N-1),(0 0 0...1 0)时,....

办法1:计算从指数

计算产品的长度在homopol(length1*length2*...*lengthN).然后,迭代,我从零到的产品。现在,索引你想要的是我%length1,(i/length1)%length2,(i/length1/length2)%length3,...

办法2:递归

我打到它,看到nikie的答复。:-)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top