【问题标题】:Passing hash slice to subroutine expecting array reference将哈希切片传递给期望数组引用的子程序
【发布时间】:2017-06-25 01:44:18
【问题描述】:

考虑以下 perl 脚本:

use strict;
use warnings;
use Data::Dumper;

# Expects an array reference and an undef replacement value
sub undef_to_value(\@$) {
    my $array_ref = shift(@_);
    my $default   = shift(@_);

    # Map each element in the array referenced by $array_ref to...
    #   If the element is defined, itself
    #   Otherwise, the $default value
    return map {
        defined($_) ? $_ : $default
    } @{ $array_ref };
}


my %grades = ("Alice" => 100, "Bob" => 85, "Carol" => 92);
my @students = ("Alice", "Bob", "Eve");

以下代码按预期工作:

my @student_grades = @grades{ @students };
@student_grades = undef_to_value(@student_grades, 0);

print Dumper(\@student_grades);
# $VAR1 = [
#           100,
#           85,
#           0
#         ];

但是,尝试传递哈希切片会导致 Type of arg 1 to main::undef_to_value must be array (not hash slice)

my @student_grades = undef_to_value( @grades{ @students }, 0 );

如何通过赋值将哈希切片哄入数组中,但不能在子例程调用期间?
有没有办法让失败的例子作为一个单一的任务工作?

【问题讨论】:

  • 您使用的是哪个 Perl 版本?
  • defined($_) ? $_ : $default 通常写成$_ // $default。另请注意,应避免使用子例程原型,因为正如您所见,它们几乎肯定不会做您希望它们做的事情。它们旨在编写模拟内置 Perl 运算符行为的库函数,而这肯定不是您在此处所做的。
  • // 从 5.10 开始可用。
  • 我必须记住//,它似乎很有用。至于避免原型,我正在编写的代码可能会被那些不太精通 perl 的人使用和修改。原型帮助我避免了很多关于我需要检查哪种边缘情况的猜测。

标签: arrays perl hashmap slice


【解决方案1】:

hash slice 怎么可以通过赋值哄成数组

不是。列表上下文中的哈希切片计算为多个标量,列表赋值运算符对此非常满意。

换句话说,

@L{qw( a b )} = @R{qw( a b )};

等价于

($L{a}, $L{b}) = ($R{a}, $R{b});

正如您的评论所说,undef_to_value 需要一个数组引用。如果您提供一个数组,原型会提供此功能,但您提供的是“哈希切片”。这不是变量类型,因此您不能引用它[1]。 p>

只接受标量:

sub undef_to_value {
    my $default = shift(@_);
    return map { $_ // $default } @_;
}

my @student_grades = undef_to_value(0, @grades{ @students });

当然,你可以简单地使用

my @student_grades = map { $_ // 0 } @grades{ @students };

  1. \@h{LIST} 等同于 map { \$_ } @h{LIST}

【讨论】:

  • 看来我需要更新我的data types。我没有意识到数组和列表之间有这样的区别。我错误地认为切片返回数组,但似乎它们返回列表。遗憾的是,列表似乎不是子例程原型的有效数据类型。
  • 没有列表数据类型。列表通常是指堆栈上的标量或逗号运算符。
  • Re "我错误地认为切片返回数组",像所有其他子和运算符一样,它们在标量上下文中返回一个标量,在列表上下文中返回零个或多个标量。 (有一些例外情况,数组或哈希实际上是传递给运算符而不是被评估。这有点像\@ 原型。一个例子是@a = ...。)
【解决方案2】:

您可以通过引用然后取消引用任意列表来欺骗原型

undef_to_value( @{[@grades{ @students }]}, 0 );

但这只会修改您输入的副本,因此没有太大帮助。

undef_to_value( @{[@grades{ @students }]}, 0 );
print Dumper([@grades{@students}]);
---
$VAR1 = [
    100,
    85,
    undef
        ];

幸运的是,您的undef_to_value 函数还返回了更新值的集合,所以您可以说

@grades{@students} = undef_to_value( @{[@grades{ @students }]}, 0 );
print Dumper(\%grades);
---
$VAR1 = {
          'Bob' => 85,
          'Carol' => 92,
          'Eve' => 0,
          'Alice' => 100
        };

【讨论】:

    猜你喜欢
    • 2020-06-14
    • 2016-12-04
    • 2013-03-11
    • 2013-09-27
    • 1970-01-01
    • 1970-01-01
    • 2011-06-21
    • 2019-03-29
    相关资源
    最近更新 更多