在Perl中有条件地解除引用

时间:2016-05-25 02:55:14

标签: perl reference

我有一个标量,可能是也可能不是对数组的引用。如果它是对数组的引用,我想取消引用它并迭代它。如果没有,我想将它视为一个单元素数组并迭代它。

my $result = my_complicated_expression;
for my $value (ref($result) eq 'ARRAY' ? @$result : ($result)) {
    # Do work with $value
}

目前,我有上面的代码,工作正常,但感觉笨重,而不是非常Perlish。如果值不是我期望的值,是否有更简洁的方式来表达使用回退行为取消引用值的想法?

4 个答案:

答案 0 :(得分:4)

只需在循环之前强制它。

有限的,已知的ref类型

my $result = *some function call* // [];
$result    = [$result] if ref $result ne 'ARRAY';

for my $val ( @$result ){
   print $val;
}

参考类型未知

#!/usr/bin/perl
use 5.012;

use strict;
no warnings;

sub array_ref;

my $result = [qw/foo bar foobar/];
 # $result = 'foo';         # scalar test case 
 # $result = {foo=>q{bar}}; # hash   test case
$result = array_ref $result;

for my $val ( @$result ){
  say $val;
}

sub array_ref {
  my $ref = shift;
  given(ref $ref){
    $ref = [%$ref] when('HASH');
    $ref = [$ref]  when(['SCALAR','']);
    when('ARRAY'){}
    default {
      die 'Did not prepare for other ref types';
    }
  }
  return $ref;
}

这是出于演示目的(您不应该在生产代码中使用given / when),但是显示您可以轻松地测试ref类型并转换新响应。但是,如果你真的不知道你的函数返回什么类型的变量,你怎么肯定它甚至是一个参考。如果是数组或散列怎么办?

答案 1 :(得分:3)

作为perl,对于'对'将会有几个答案。一个是品味问题 - 恕我直言,一个可接受的缩短涉及依赖于the ref function returns the empty string如果给出它的表达式是标量的事实。这意味着如果您知道只有两种可能性(即标量值和数组引用),则您不需要eq 'ARRAY'

其次,您可以迭代单个标量值(显然产生1次迭代),因此您不必将$result括号放在"标量"情况下。

将这两个小的简化用于给予;

use v5.12;

my $result1 = "Hello World";
my $result2 = [ "Hello" , "World" ];

for my $result ($result1, $result2) {
    for my $value ( ref $result ? @$result : $result) {
        say $value ;
    }
}

产生;

Hello World
Hello
World

可能会有更多爱好者。你可以做的事情,但这似乎是简洁和可读之间的合理妥协。当然,YMMV。

答案 2 :(得分:1)

我看到我迟到了,但我无法帮助它。使用eval$@以及逗号运算符

my $ra = [ qw(a b c) ];
my $x = 23;

my $var = $ra;
# my $var = $x;    # swap comment to test the other

foreach my $el ( eval { @{$var} }, $@ && $var ) 
{
    next if $el =~ /^$/;  # when @$var is good comma adds empty line
    print $el, "\n";
}

打印a b c(每行一个),如果我们切换到my $var = $x,则会打印23

$var有引用时,$@为空,但逗号仍然执行,这会在循环中添加一个空行,即next。作为跳过空行的替代方法,可以将其过滤掉

foreach my $el ( grep { !/^$/ } eval { @{$var} }, $@ && $var ) 

此外,还可清除空行。但是,大部分时间都是可取的。

答案 3 :(得分:-1)

sub deref {
    map ref($_) eq 'ARRAY'? @$_ : ref($_) eq 'HASH'? %$_ : $_, @_
}

sub myCompExpr {
    1, 2, 3, [4, 5, 6], {Hello => 'world', Answer => 42}
}

print $_ for deref myCompExpr