Почему этот оператор рассматривается как строка, а не ее результат? - PullRequest
5 голосов
/ 06 апреля 2010

Я пытаюсь выполнить фильтрацию на основе композиции для большого набора строк (белковых последовательностей).
Я написал группу из трех подпрограмм, чтобы позаботиться об этом, но у меня две проблемы - одна второстепенная, одна главная. Незначительная проблема в том, что когда я использую List :: MoreUtils 'pairwise' , я получаю предупреждения об использовании $a и $b только один раз, и они не инициализируются. Но я считаю, что я вызываю этот метод правильно (основываясь на записи CPAN для него и некоторых примерах из Интернета).
Главная проблема - ошибка "Can't use string ("17/32") as HASH ref while "strict refs" in use..."

Кажется, что это может произойти, только если цикл foreach в &comp передает значения хеш-функции в виде строки вместо оценки операции деления. Я уверен, что сделал ошибку новичка, но не могу найти ответ в Интернете. Первый раз, когда я даже посмотрел на код perl, был в прошлую среду ...

use List::Util;
use List::MoreUtils;
my @alphabet = (
 'A', 'R', 'N', 'D', 'C', 'Q', 'E', 'G', 'H', 'I',
 'L', 'K', 'M', 'F', 'P', 'S', 'T', 'W', 'Y', 'V'
);
my $gapchr = '-';
# Takes a sequence and returns letter => occurrence count pairs as hash.
sub getcounts {
 my %counts = ();
 foreach my $chr (@alphabet) {
  $counts{$chr} = ( $_[0] =~ tr/$chr/$chr/ );
 }
 $counts{'gap'} = ( $_[0] =~ tr/$gapchr/$gapchr/ );
 return %counts;
}

# Takes a sequence and returns letter => fractional composition pairs as a hash.
sub comp {
 my %comp = getcounts( $_[0] );
 foreach my $chr (@alphabet) {
  $comp{$chr} = $comp{$chr} / ( length( $_[0] ) - $comp{'gap'} );
 }
 return %comp;
}

# Takes two sequences and returns a measure of the composition difference between them, as a scalar.
# Originally all on one line but it was unreadable.

sub dcomp {
 my @dcomp = pairwise { $a - $b } @{ values( %{ comp( $_[0] ) } ) }, @{ values( %{ comp( $_[1] ) } ) };
 @dcomp = apply { $_ ** 2 } @dcomp;
 my $dcomp = sqrt( sum( 0, @dcomp ) ) / 20;
 return $dcomp;
}

Большое спасибо за любые ответы или советы!

Ответы [ 4 ]

4 голосов
/ 06 апреля 2010

В вашем коде есть несколько ошибок. Во-первых, обратите внимание на perldoc perlop :

Поскольку таблица транслитерации создается во время компиляции, , ни SEARCHLIST, ни REPLACEMENTLIST не подвергаются двойной интерполяции .

Так что ваш метод подсчета неверен. Я также считаю, что вы злоупотребляете pairwise. Трудно оценить, что представляет собой правильное использование, потому что вы не приводите примеры того, что вы должны получить, используя некоторые простые входные данные.

В любом случае, я бы переписал этот скрипт следующим образом (в нем есть несколько операторов отладки):

#!/usr/bin/perl

use List::AllUtils qw( sum );
use YAML;

our ($a, $b);
my @alphabet = ('A' .. 'Z');
my $gap = '-';

my $seq1 = 'ABCD-EFGH--MNOP';
my $seq2 = 'EFGH-ZZZH-KLMN';

print composition_difference($seq1, $seq2);

sub getcounts {
    my ($seq) = @_;
    my %counts;
    my $pattern = join '|', @alphabet, $gap;
    $counts{$1} ++ while $seq =~ /($pattern)/g;
    warn Dump \%counts;
    return \%counts;
}

sub fractional_composition_pairs {
    my ($seq) = @_;
    my $comp = getcounts( $seq );
    my $denom = length $seq - $comp->{$gap};
    $comp->{$_} /= $denom for @alphabet;
    warn Dump $comp;
    return $comp;
}

sub composition_difference {
    # I think your use of pairwise in the original script
    # is very buggy unless every sequence always contains
    # all the letters in the alphabet and the gap character.
    # Is the gap character supposed to factor in the computations here?

    my ($comp1, $comp2) = map { fractional_composition_pairs($_) } @_;
    my %union;
    ++ $union{$_} for (keys %$comp1, keys %$comp2);

    my $dcomp;
    {
        no warnings 'uninitialized';
        $dcomp = sum map {
            ($comp1->{$_} - $comp2->{$_}) ** 2
        } keys %union;
    }

    return sqrt( $dcomp ) / 20; # where did 20 come from?
}
2 голосов
/ 06 апреля 2010

Проходя через код, который вы предоставили, я бы так написал. Я не знаю, будет ли это работать так, как вы хотели, чтобы все работало.

use strict;
use warnings;
our( $a, $b );

use List::Util;
use List::MoreUtils;

my @alphabet = split '', 'ARNDCQEGHILKMFPSTWYV';
my $gapchr = '-';
# Takes a sequence and returns letter => occurrence count pairs as hash.
sub getcounts {
  my( $sequence ) = @_;
  my %counts;
  for my $chr (@alphabet) {
    $counts{$chr} = () = $sequence =~ /($chr)/g;
    # () = forces list context
  }
  $counts{'gap'} = () = $sequence =~ /($gapchr)/g;
  return %counts if wantarray; # list context
  return \%counts; # scalar context
  # which is what happens inside of %{  }
}

# Takes a sequence and returns letter => fractional composition pairs as a hash
sub comp {
  my( $sequence ) = @_;
  my %counts = getcounts( $sequence );
  my %comp;
  for my $chr (@alphabet) {
    $comp{$chr} = $comp{$chr} / ( length( $sequence ) - $counts{'gap'} );
  }
  return %comp if wantarray; # list context
  return \%comp; # scalar context
}

# Takes two sequences and returns a measure of the composition difference
#   between them, as a scalar.
sub dcomp {
  my( $seq1, $seq2 ) = @_;
  my @dcomp = pairwise { $a - $b }
    @{[ values( %{ comp( $seq1 ) } ) ]},
    @{[ values( %{ comp( $seq2 ) } ) ]};
  # @{[ ]} makes a list into an array reference, then dereferences it.
  # values always returns a list
  # a list, or array in scalar context, returns the number of elements
  # ${  } @{  } and %{  } forces their contents into scalar context

  @dcomp = apply { $_ ** 2 } @dcomp;
  my $dcomp = sqrt( sum( 0, @dcomp ) ) / 20;
  return $dcomp;
}

Одна из самых важных вещей, которую вам нужно знать, это различия между скалярным, списочным и пустым контекстами. Это потому, что все ведет себя по-разному, в разных контекстах.

2 голосов
/ 06 апреля 2010

%{ $foo } будет обрабатывать $foo как ссылку на хеш и разыменовывать ее; аналогично, @{} будет разыменовывать ссылки на массивы. Поскольку comp возвращает хеш в виде списка (хеш становится списком при передаче в функции и из функций), а не в качестве ссылки на хеш, %{} неверен. Вы можете опустить %{}, но values - это специальная форма, для которой нужен хеш, а не хеш, переданный в виде списка. Чтобы передать результат comp в values, comp необходимо вернуть ссылку на хеш, которая затем разыменовывается.

Есть еще одна проблема с вашим dcomp, а именно, что порядок values (как говорится в документации ) "возвращается в явно случайном порядке", поэтому значения передаются в pairwise блок не обязательно для одного и того же персонажа. Вместо values вы можете использовать хеш-фрагменты. Теперь мы вернулись к comp возвращению хэша (в виде списка).

sub dcomp {
    my %ahisto = comp($_[0]);
    my %bhisto = comp($_[1]);
    my @keys = uniq keys %ahisto, keys %bhisto;
    my @dcomp = pairwise { $a - $b } , @ahisto{@keys}, @bhisto{@keys};
    @dcomp = apply { $_ ** 2 } @dcomp;
    my $dcomp = sqrt( sum( 0, @dcomp ) ) / 20;
    return $dcomp;
}

Это не относится к тому, что происходит, если персонаж появляется только в одном из $_[0] и $_[1].

uniq оставлено в качестве упражнения для читателя.

1 голос
/ 06 апреля 2010

re: незначительные проблемы

Это нормально и часто встречается с (некоторыми) модулями List::Util и List::MoreUtils.

Одним из способов удаления предупреждений является простое объявление этих special variables заранее, например:

our ($a, $b);

Другим было бы предшествовать pairwise с:

no warnings 'once';

См. perlvar для получения дополнительной информации о $ a и $ b

/ I3az /

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...