Исходный код
@lib_001=('A','G','C','T','A');
@lib_002=('C','T','A','A','G');
@lib_003=('T','G','C','A','T');
$string=<FILE>;
@sequence=split("",$string);
@code=@sequence[-7..-3];
if((join('',@lib_001) eq join('',@code)) or
(join('',@lib_001[0..3]) eq join('',@code[0..3])) or
(join('',@lib_001[1..4]) eq join('',@code[1..4])) or
(join('',@lib_001[0,1,2,4]) eq join('',@code[0,1,2,4])) or
(join('',@lib_001[0,2,3,4]) eq join('',@code[0,2,3,4])) or
(join('',@lib_001[0,1,3,4]) eq join('',@code[0,1,3,4])))
{$tag=join('',@code);
$closest_tag=join('',@lib_001);}
elsif((join('',@lib_002) eq join('',@code)) or
(join('',@lib_002[0..3]) eq join('',@code[0..3])) or
(join('',@lib_002[1..4]) eq join('',@code[1..4])) or
(join('',@lib_002[0,1,2,4]) eq join('',@code[0,1,2,4])) or
(join('',@lib_002[0,2,3,4]) eq join('',@code[0,2,3,4])) or
(join('',@lib_002[0,1,3,4]) eq join('',@code[0,1,3,4])))
{$tag=join('',@code);
$closest_tag=join('',@lib_002);}
elsif((join('',@lib_003) eq join('',@code)) or
(join('',@lib_003[0..3]) eq join('',@code[0..3])) or
(join('',@lib_003[1..4]) eq join('',@code[1..4])) or
(join('',@lib_003[0,1,2,4]) eq join('',@code[0,1,2,4])) or
(join('',@lib_003[0,2,3,4]) eq join('',@code[0,2,3,4])) or
(join('',@lib_003[0,1,3,4]) eq join('',@code[0,1,3,4])))
{$tag=join('',@code);
$closest_tag=join('',@lib_003);}
else
{$tag=join('',@code);
$closest_tag='NONE';}
Как можно улучшить исходный код
Нетрудно видеть, что в исходном коде трижды повторяется реализация проверки некоего условия. Спрячу эту проверку в отдельную функцию:
sub isTagLike (\@\@) {
my ( @lib, @code ) = @_;
return (join('',@lib) eq join('',@code)) or
(join('',@lib[0..3]) eq join('',@code[0..3])) or
(join('',@lib[1..4]) eq join('',@code[1..4])) or
(join('',@lib[0,1,2,4]) eq join('',@code[0,1,2,4])) or
(join('',@lib[0,2,3,4]) eq join('',@code[0,2,3,4])) or
(join('',@lib[0,1,3,4]) eq join('',@code[0,1,3,4]));
}
if (isTagLike( @lib_001, @code )) {
$tag = join('', @code);
$closest_tag = join('', @lib_001);
}
elsif ...
После анализа собственно алгоритма сравнения становится видно, что два параметра считаются совпадающими, если они совпадают в точности, либо без учёта одного из пяти составляющих элементов.
Это позволяет переписать алгоритм проверки в виде цикла и ещё одной функции.
Легко видеть, что дублируется реализация сравнения и запоминания последовательности — её тоже можно легко переписать в виде цикла.
Также сильно хочется избавиться от расклеивания и склеивания строк.
sub isTagLikeWithout {
my ( $tag, $code, $position ) = @_;
substr( $tag, $position, 1, '*' );
substr( $code, $position, 1, '*' );
return $tag eq $code;
}
sub isTagLike {
my ( $tag, $code ) = @_;
if ( $tag eq $code ) return true;
foreachmy $i (0 .. 4) {
if ( isTagLikeWithout( $tag, $code, $i ) ) return true;
}
return false;
}
@lib = ( 'AGCTA', 'CTAAG', 'TGCAT' );
$string=<FILE>;
$code=substr($string, -7, -3);
foreachmy $lib (@lib) {
if ( isTagLike( $lib, $code ) ) {
$tag = $code;
$closest_tag = $lib;
last;
}
}
После рефакторинга знание о реализации сравнения генетической информации переехало в аж две отдельные функции (так как само по себе нетривиально) — что тоже очень хорошо. Остались в одной куче знания о способе получения входной последовательности (из файла), сопоставляемых образцах (@lib
) и способе хранения результата сравнения ($tag
и $closest_tag
), но это пока не так критично.