ループをハードコーディングせずに複数のリストの組み合わせを作成するにはどうすればよいですか?
-
22-07-2019 - |
質問
次のようなデータがあります:
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'.
やりたいことは
-
part1
からpartK
までの要素のすべての組み合わせを生成します
-
@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)、同じ結果を得るための柔軟でコンパクトな方法が必要です。何かありますか?
Algorithm :: Loops 、しかしそれを達成する方法がわからない。
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][ Set :: CrossProduct
をお勧めします、すべてのセットの外積を生成するイテレータを作成します。イテレータを使用するため、事前にすべての組み合わせを生成する必要はありません。むしろ、要求に応じてそれぞれを生成します。
<*>], $prob[$i][ Set :: CrossProduct
をお勧めします、すべてのセットの外積を生成するイテレータを作成します。イテレータを使用するため、事前にすべての組み合わせを生成する必要はありません。むしろ、要求に応じてそれぞれを生成します。
<*>]]
for 0 .. @{$homopol[$i]} - 1;
};
my $iterator = Set::CrossProduct->new([ @combined ]);
while( my $tuple = $iterator->get ){
my @h = map { Set :: CrossProduct
をお勧めします、すべてのセットの外積を生成するイテレータを作成します。イテレータを使用するため、事前にすべての組み合わせを生成する必要はありません。むしろ、要求に応じてそれぞれを生成します。
<*>->[0] } @$tuple;
my @p = map { Set :: CrossProduct
をお勧めします、すべてのセットの外積を生成するイテレータを作成します。イテレータを使用するため、事前にすべての組み合わせを生成する必要はありません。むしろ、要求に応じてそれぞれを生成します。
<*>->[1] } @$tuple;
my $product = 1;
$product *= Set :: CrossProduct
をお勧めします、すべてのセットの外積を生成するイテレータを作成します。イテレータを使用するため、事前にすべての組み合わせを生成する必要はありません。むしろ、要求に応じてそれぞれを生成します。
<*> for @p;
print join('-', @h), ' ', join(' x ', @p), ' = ', $product, "\n";
}
他のヒント
入力データを変更せずに Algorithm :: Loops を使用するソリューションは次のようになります。 :
use Algorithm::Loops;
# Turns ([a, b, c], [d, e], ...) into ([0, 1, 2], [0, 1], ...)
my @lists_of_indices = map { [ 0 .. @入力データを変更せずに Algorithm :: Loops を使用するソリューションは次のようになります。 :
[
{ T => 1.00, C => 0.63, CC => 0.002, G => 0.83 },
{ T => 0.72, TT => 0.03, ... },
...
]
しかし、構造をもっと似たようなものに変更することで、実際にコードを明確にすることができると思います
<*>
並列データ構造がないため、インデックスを繰り返してから2つの異なる場所でそれらのインデックスを検索する代わりに、利用可能な基本シーケンスを単純に繰り返すことができます。
] } @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;
});
しかし、構造をもっと似たようなものに変更することで、実際にコードを明確にすることができると思います
<*>並列データ構造がないため、インデックスを繰り返してから2つの異なる場所でそれらのインデックスを検索する代わりに、利用可能な基本シーケンスを単純に繰り返すことができます。
なぜ再帰を使用しないのですか?深さをパラメーターとして渡し、関数がループ内でdepth + 1を使用して自分自身を呼び出すようにします。
これは、@ homopol配列(N say)と同じ長さのインデックスの配列を作成することで実行でき、見ている組み合わせを追跡できます。実際、この配列は 基数Nの数値。要素は数字です。基数Nに連続した数字を書き留めるのと同じ方法で繰り返します(例:(0 0 0 ... 0)、(0 0 0 ... 1)、...、(0 0 0 ... N- 1)、(0 0 0 ... 1 0)、....
アプローチ1:インデックスからの計算
ホモポールの長さの積を計算します(length1 * length2 * ... * lengthN)。次に、iをゼロから積まで繰り返します。これで、必要なインデックスはi%length1、(i / length1)%length2、(i / length1 / length2)%length3、...
アプローチ2:再帰
私はそれにbeatられました、ニキーの答えを見てください。 :-)