Как передать массив Perl в виде скаляра в подпрограмму?

Здесь новоиспеченный Perl-разработчик. Я ломал себе голову и искал в Интернете, пытаясь понять это ... раздраженный, я пришел к вам в поисках ясности.

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

my @arrMissingTids;
@arrMissingTids = %hshTids;

my $missingtid;
foreach $missingtid (@arrMissingTids) {
    print "$missingtid\n";
}

Это хорошо работает, возвращая значения, которые я хочу видеть в массиве:

500000246,500000235,500000185,500000237,500000227,500000252

Однако, когда я передаю это подпрограмме и включаю ее в имя переменной, она не предоставляет список, как написано выше, а просто номер 1. Код для этого ниже:

myqry(@arrMissingTids);

sub myqry($) {

    my $missingtids = @_;

    $sql = "select 
        i.tid i_tid, i.name i_name
        from 
        instrument i
        where i.tid in ($missingtids)";

    print "$sql/n";
}

Print $sql возвращает следующее:

Select i.tid i_tid, i.name i_name from instrument i where i.tid in (1)

Когда я хочу, чтобы он вернул следующее:

Select i.tid i_tid, i.name i_name from instrument i where i.tid in (500000246,500000235,500000185,500000237,500000227,500000252)

Заранее спасибо за любые указатели в правильном направлении!


person Scott Holtzman    schedule 09.05.2012    source источник
comment
Почему прототипы функций Perl 5 плохие?   -  person daxim    schedule 09.05.2012
comment
Я вынужден упомянуть, что создание SQL-запроса с помощью интерполяции представляет собой угрозу безопасности. bobby-tables.com   -  person Joel Berger    schedule 09.05.2012
comment
это из-за ($), см. мой ответ;)   -  person CodeClown42    schedule 09.05.2012


Ответы (6)


Проблема именно здесь:

my $missingtids = @_;

Вы вызываете массив @_ в скалярном контексте. Это означает, что вы назначаете $missingtids количество элементов в @_. Один из способов обойти это — передать ссылку на массив:

sub myqry {

    my $missingtids_ref = shift;
    my @missingtids=@$missingtids_ref;

    $sql = "select 
        i.tid i_tid, i.name i_name
        from 
        instrument i
        where i.tid in (" . join(",",@missingtids) . ")";

    print "$sql/n";
}

Для получения дополнительной информации см. perldoc perlref и perldoc perldata.

person Community    schedule 09.05.2012
comment
приняв это как ответ, поскольку он был первым, и решил проблему ... но отличная помощь во всем. я очень ценю все отзывы! - person Scott Holtzman; 09.05.2012

Здесь есть три проблемы. Первый использует прототип функции, просто оставьте его, см. Почему Perl 5 прототипы функций плохие?.

Второй — несоответствие типов в вызове функции и на стороне получателя самой функции. Либо используйте массивы оба раза, либо ссылки на массивы оба раза.

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

myqry(@arrMissingTids);
sub myqry {
    my @missingtids = @_;
    $sql = "select
        i.tid i_tid, i.name i_name
        from
        instrument i
        where i.tid in (" . join(',', ('?') x @missingtids) . ")";
    print "$sql\n";
    # $dbh->selectall_arrayref($sql, {}, @missingtids)
}

myqry(\@arrMissingTids);
sub myqry {
    my @missingtids = @{ shift() };
    $sql = "select
        i.tid i_tid, i.name i_name
        from
        instrument i
        where i.tid in (" . join(',', ('?') x @missingtids) . ")";
    print "$sql\n";
    # $dbh->selectall_arrayref($sql, {}, @missingtids)
}
person daxim    schedule 09.05.2012
comment
Изменен код для работы с заполнителями базы данных. Напоминаем, Джоэл! - person daxim; 10.05.2012
comment
Спасибо Даксим. Я ценю этот маленький уголок о сдаче SQL. На крутой кривой обучения, так что ценю всю помощь. - person Scott Holtzman; 11.05.2012

Если кто-то еще не упомянул об этом, прототип здесь является проблемой:

sub myqry($) {

Учти это:

sub test1($) {
    print "$_\n" foreach @_;
}

sub test2 {
    print "$_\n" foreach @_;
}

my @args = ('a', 'b', 'c');

test1(@args);
test2(@args);      

и вывод:

3
a
b
c

К настоящему времени вы поняли, что массив в скалярном контексте — это просто количество элементов, например:

my $n = @args;

$n равно 3. Передавая массив в подпрограмму, которая сводит его к скаляру, вы получаете один аргумент — количество элементов в массиве. Затем вы делаете это:

my $missingtids = @_;

который всегда будет только один из-за ($) в подопределении (массив уже уменьшен до одного элемента). Следовательно, вы получаете 1.

$0,02: прототипы IMO на Perl — плохая идея ;)

person CodeClown42    schedule 09.05.2012
comment
возможно, упомянуть прототип (\@)? - person Joel Berger; 09.05.2012
comment
Я сам ими не пользуюсь, но это выглядит лучше, чем ($). - person CodeClown42; 09.05.2012

Итак, вы хотите создать строку

... in (1,2,3,4)

Используйте join

myqry(join(',', @arrMissingTids))

Но это скорее наизнанку. Это было бы лучше:

sub myqry {
    my $missingtids = join(',', @_);

    return "select 
        i.tid i_tid, i.name i_name
        from 
        instrument i
        where i.tid in ($missingtids)
    ";
}

myqry(@arrMissingTids);
person ikegami    schedule 09.05.2012

Спасибо за помощь. После того, как все было сказано и сделано, я в конечном итоге избавился от прототипа и использовал этот код, который отлично работал и был собран из всей справки выше:

myqry(@arrTids);

sub myqry {
   $missingtids = join(",",@_);

   .rest of code...
}
person Scott Holtzman    schedule 11.05.2012

Непроверенный, но, вероятно, правильный:

myqry(\@arrMissingTids);

sub myqry($) {

    my $missingtids = shift; # or $_[0]

    $sql = "select 
        i.tid i_tid, i.name i_name
        from 
        instrument i
        where i.tid in (" . join(',', @{$missingtids}) . ")";

    print "$sql/n";
}

Конечно, вы можете передать сам массив вместо ссылки, но тогда вам нужно будет изменить прототип и переписать ссылки на массив. Но то, что выше, должно вас заинтересовать.

person Marius Kjeldahl    schedule 09.05.2012
comment
Лучше не использовать прототип в функции. Почти во всех случаях ломает гораздо больше вещей, чем решает. Просто избегайте их. - person LeoNerd; 09.05.2012
comment
или, по крайней мере, используйте прототип (\@), чтобы заставить массивы ссылаться на массивы, а не для принудительного скалярного контекста, что почти наверняка не то, что здесь требуется. - person Joel Berger; 09.05.2012
comment
@goldilocks, я не понимаю, вы имеете в виду, что myqry(\@array) не будет работать, если myqry будет прототипом ($)? Должно. - person Joel Berger; 09.05.2012
comment
Упс! Виноват. Я не заметил ссылку или последующее @{$dereference}. Извиняюсь. - person CodeClown42; 09.05.2012