【问题标题】:Loop through an anonymous array inside perl hash循环遍历 perl 哈希中的匿名数组
【发布时间】:2013-06-24 03:00:42
【问题描述】:

背景:我是 Perl 的新手,我正在修改一个简单的脚本,该脚本循环遍历一个匿名数组的哈希值。

问题我似乎无法遍历数组。我得到的只是ARRAY(0x1663b78)

守则

#!/usr/bin/perl
package Foo;
use strict "vars";

sub new {
    my $class = shift;
    my $self = {
        distro  => "",
        pkg_mgr => "",
        options => ["PHP + Apache", "PHP + Lighthttpd", "PHP + Nginx", "RubyGems + Rails", "Node JS + NPM"]
    };

    bless $self, $class;

    return $self;
}

sub print_options {
    my($self) = @_;
    foreach($self->{options}) {
        print $_ . "\n";
    }
}

my $setup = new Foo();
$setup->print_options();

另外,如果我真的不需要私有或公共变量,如果有人可以告诉我使用use strict "vars"; 是否远程有用。我知道我会如何在 PHP 中做到这一点,但我无法在 Perl 中解决这个问题。

最后,我使用 Perl 的原因是因为最终我将制作一个为服务器(包括 PHP)安装软件的应用程序。该脚本需要通过命令行进行用户交互。

【问题讨论】:

  • 你应该只做use strict; - 没有理由做use strict "vars";,这只会带来一些限制。
  • @friedo,感谢您的提示!

标签: perl loops hashmap


【解决方案1】:

一些关于 Perl 的小提示:

  • 遇到此类问题时使用Data::DumperData::Dumper 将帮助您了解结构。
  • 如果您打算完全面向对象,那就全力以赴。你有构造函数和方法。方法应该返回值,并且您对这些值进行操作。每个方法和构造函数都应尽可能独立,并且对您的整体对象结构知之甚少。
  • main 包中运行。你应该翻转你的程序。最后两个语句应该在顶部,并在下面定义您的包。

如果你使用Data::Dumper 并在调用它时转储$self 的数据,你会发现$self 是一个包含三个键的散列,并且options 键指向一个匿名数组。

为了做你想做的事,你必须取消引用那个匿名数组:

sub print_options {
    my($self) = @_;
    for ( @{$self->{options} } ) {
        print $_ . "\n";
    }
}

但是,让我们清理您的程序以使其成为一个完整的类。首先,方法从不打印——它们返回值。因此,您不应该有 print_options 方法。相反,它应该返回数组(或对数组的引用)。

此外,您的构造函数不必知道您的Options 的结构。如果你改变它呢?让我们看一下更改后的程序:

#! /usr/bin/env perl
#
use warnings;
use strict;
use feature qw(say);

my $setup = Foo->new;
for my $option ( $setup->Option ) {
    say $option;
}

package Foo;

sub new {
    my $class = shift;
    my $self = {};
    bless $self, $class;
    $self->Option("PHP + Apache");
    $self->Option("PHP + Lighthttpd");
    $self->Option("PHP + Nginx");
    $self->Option("RubyGems + Rails");
    $self->Option("Node JS + NPM");
    return $self;
}

sub Distro {
    my $self   = shift;
    my $distro = shift;
    if ( defined $distro ) {
        $self->{DISTRO} = $distro;
    }
    return $self->{DISTRO};
}

sub Package_Manager {
    my $self    = shift;
    my $package = shift;
    if ( defined $package ) {
        $self->{PACKAGE} = $package;
    }
    return $self->{PACKAGE};
}

sub Option {
    my $self = shift;
    my $option = shift;;

    if ( not exists $self->{OPTION} ) {
        $self->{OPTION} = [];
    }

    if ( defined $option ) {
        push @{ $self->{OPTION} }, $option;
    }
    my @array = @{ $self->{OPTION} };
    return wantarray ? @array : \@array;
}

首先,与 PHP 不同,Perl 在执行之前编译代码。因此,您可以在程序底部定义包及其子例程。以上几行代码在默认的main包中运行。如果我确实在 Foo 包中使用了包变量,我不会不小心在我的程序中使用它。

请注意,我的方法或构造函数都不知道Foo 对象的结构。当我在new 构造函数中设置选项时,我调用Option 方法进行设置。这样,如果我更改存储选项的方式,我只需要更改我的 Option 方法,而不必担心我的包中的其他地方必须更改它。它还简化了我的程序并使其更易于支持。

通过使我的Option 方法设置或获取我的选项,我简化了代码。我可以看到我将选项存储在一个匿名数组中,很明显我需要在返回对象时 dereference 它。我喜欢使用wantarray 并让用户选择是想要数组还是引用数组。

请注意,我不会简单地返回 $self->{OPTION}。它是对数组的引用,应该可以工作,但是如果我返回它,我将返回该对象的内存位置!因此,如果用户更改了该引用,他们将更改我的对象而无需通过我的方法。因此,我创建了另一个数组,并返回对它的引用。随心所欲地使用那个数组引用,你并没有改变我的对象。

此代码不完整。我可以设置选项,但不能取消设置选项。有一种方法来 pushpop 选项可能会很好。相反,此时您所能做的就是返回整个列表,而不是修改它。

【讨论】:

  • +1 以获得清晰和文档参考。还有两个问题:为什么要遵循数组?那有什么作用?此外,Perl OOP 比我习惯使用 PHP 的要抽象一点,那么“全力以赴”到底是什么意思?在 PHP 中我会遵循这个过程:http://pastebin.com/iZ1gUfAm
  • @djthoms 引用只是对象的内存位置。例如,我的$self->{OPTION} 仅指向我存储数组的内存位置。因此ARRAY(0x1663b78) 表示您将数组存储在内存位置0x1663b78。您想要的不是内存位置,而是该内存位置的数组。 reference 接受一个数组并为您提供内存中的位置。 de引用获取内存位置并返回数组。
  • @djthoms 我在答案中添加了 Foo 包应该如何写为 full bore 包。我的方法返回值而不是打印它们。这是我的程序要做的。我班级的各个部分对班级的整个结构了解有限。我的构造函数不知道我如何存储选项。我的 Option 方法不知道我如何存储 Distro。将所有东西分开并有一个目的使编写类更简洁,更易于维护。
  • @djthoms Perl 对象没有真正的私有方法。有一个社区协议,你不使用未记录的方法,你不直接在结构上操作,就像有一个商定的社会契约,你不偷看打开的窗户,因为有人忘了拉百叶窗。通常,如果某些内容以下划线开头,则它是私有的,这是可以理解的。但是,您应该使用 [POD](perldoc.perl.org/perlpod.html) 来记录您的课程。然后,如果没有记录某些内容。这是私人的。
  • 这是一个非常棒的答案。我唯一的批评是 $self->{OPTION} = [] unless exists $self->{OPTION} 是多余的,这要归功于自动生存。
【解决方案2】:

$self->{options} 不是数组,而是数组的引用。您需要取消引用它才能遍历其值。

my @options = @{ $self=>{options} };

【讨论】:

    【解决方案3】:

    您需要进行以下更改。

    foreach(@{$self->{options}}) {
            ^^                ^
    

    optionsarrayref 的密钥,您需要取消引用它才能使用它。

    至于你的另一个问题,建议使用更通用的一对编译指示,尤其是对于初学者

    use strict;
    use warnings;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-20
      • 2013-02-09
      • 1970-01-01
      • 2016-01-27
      • 2017-02-09
      • 1970-01-01
      • 2012-07-27
      • 2012-08-22
      相关资源
      最近更新 更多