Можете ли вы заставить скалярную или массивную ссылку быть массивом в Perl?

#perl #arrays #reference #scalar

Вопрос:

У меня есть переменная perl $results , которая возвращается из службы. Предполагается, что значение должно быть массивом и $results должно быть ссылкой на массив. Однако, если в массиве есть только один элемент, ему $results будет присвоено это значение, а не ссылочный массив, содержащий этот один элемент.

Я хочу сделать foreach цикл для ожидаемого массива. Без проверки ref($results) eq 'ARRAY' , есть ли какой-либо способ получить что-то эквивалентное следующему:

 foreach my $result (@$results) {
    # Process $result
}
 

Этот конкретный пример кода будет работать для справки, но будет жаловаться на простой скаляр.

ПРАВКА: Я должен уточнить, что у меня нет возможности изменить то, что возвращается из сервиса. Проблема в том, что значение будет скаляром, когда существует только одно значение, и оно будет ссылкой на массив, когда существует более одного значения.

Комментарии:

1. Такое поведение вызывает у меня желание закричать и сказать: «ГЛУПЫЙ ПЕРЛ!» Но потом я понимаю, что языки, которые не требуют этой ерунды, все еще делают это под капотом, что заставляет меня меньше беспокоиться….

Ответ №1:

я не уверен, что есть какой-то другой способ, кроме:

 $result = [ $result ]   if ref($result) ne 'ARRAY';  
foreach .....
 

Ответ №2:

Другим решением было бы обернуть вызов на сервер и заставить его всегда возвращать массив, чтобы упростить вам всю оставшуюся жизнь:

 sub call_to_service
{
    my $returnValue = service::call();

    if (ref($returnValue) eq "ARRAY")
    {
        return($returnValue);
    }
    else
    {
       return( [$returnValue] );
    }
}
 

Тогда вы всегда можете быть уверены, что получите ссылку на массив, даже если это был только один элемент.

 foreach my $item (@{call_to_service()})
{
  ...
}
 

Ответ №3:

Хорошо, если ты не можешь этого сделать…

 for my $result ( ref $results eq 'ARRAY' ? @$results : $results ) {
    # Process result
}
 

или это…

 for my $result ( ! ref $results ? $results : @$results ) {
    # Process result
}
 

тогда вам, возможно, придется попробовать что-нибудь волосатое и страшное, как это!….

 for my $result ( eval { @$results }, eval $results ) {
    # Process result
}
 

и чтобы избежать этой опасной оценки строки, она становится действительно уродливой!!….

 for my $result ( eval { $results->[0] } || $results, eval { @$results[1 .. $#{ $results }] } ) {
    # Process result
}
 

PS. Я бы предпочел абстрагировать его в примере sub ala call_to_service (), приведенном reatmon.

Комментарии:

1. Это не оценка строки. И повторение цикла (некоторое выражение, включающее @$результаты) сильно отличается от повторения цикла (@$результаты). Первый будет копировать массив (потребляя память); второй будет использовать псевдоним для него (и позволит изменять элементы с помощью переменной цикла).

2. @ysth — Есть… см.раздел «Оценка результатов». Мое предложение состояло в том, чтобы использовать пример call_to_service (), приведенный ранее. Мой ответ был чем-то вроде «остроумного решения» для ограничения, наложенного плакатом, так что да, хорошо указать на его недостатки.

Ответ №4:

Я бы пересчитал код внутри цикла, а затем сделал

 if( ref $results eq 'ARRAY' ){
    my_sub($result) for my $result (@$results);
}else{
    my_sub($results);
}
 

Конечно, я бы сделал это только в том случае, если бы код в цикле был нетривиальным.

Ответ №5:

Я только что проверил это с помощью:

 #!/usr/bin/perl -w
use strict;

sub testit {

 my @ret = ();
 if (shift){
   push @ret,1;
   push @ret,2;
   push @ret,3;
}else{
  push @ret,"oneonly";
}

return @ret;
}

foreach my $r (@{testit(1)}){
  print $r." test1n";
}
foreach my $r (@{testit()}){
   print $r." test2n";
}
 

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

Ответ №6:

Ты можешь сделать это вот так:

 my @some_array
push (@some_array, results);
foreach my $elt(@some_array){
  #do something
}
 

Комментарии:

1. Хотя этот фрагмент кода может решить вопрос, включая объяснение , действительно помогает улучшить качество вашего поста. Помните, что в будущем вы будете отвечать на этот вопрос для читателей, и эти люди могут не знать причин вашего предложения кода. Пожалуйста, также постарайтесь не загромождать свой код пояснительными комментариями, это снижает читабельность как кода, так и пояснений!