在Perl哈希中循环遍历匿名数组

3

背景:我是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)的应用程序。该脚本将需要通过命令行与用户进行交互。

2
你应该只使用 use strict; - 没有理由使用 use strict "vars";,因为它只打开了一些严格的规定。 - friedo
3个回答

9

一些小Perl提示:

  • 每当您遇到问题时,请使用 Data :: Dumper Data :: Dumper 将帮助您了解结构。
  • 如果您要全面采用面向对象的方式,则应全面采用此方式。 您具有构造函数和方法。 方法应返回值,并且您操作这些值。 每个方法和构造函数都应尽可能独立,并且对整体对象结构知识很少。
  • main 包中操作。 您应该翻转程序。 最后两个语句应位于顶部,并在下方定义您的包。

如果您调用$ 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”方法设置为设置或获取选项,我简化了代码。我可以看到我正在匿名数组中存储选项,并且很明显当我返回对象时需要对其进行解引用。我喜欢使用“wantarray”并给用户选择他们想要一个数组还是数组的引用。
请注意,我不仅仅返回“$self->{OPTION}”。它是一个数组的引用,应该起作用,但如果我返回它,我就返回了该对象的内存位置!因此,如果用户更改该引用,他们就会更改我的对象,而不是通过我的方法。因此,我创建另一个数组,并返回对其的引用。无论您如何搞乱该数组引用,都不会更改我的对象。
这段代码还没有完成。我可以设置选项,但无法取消选项。可能有一种方法可以推送和弹出选项。在此时,您只能返回整个列表,而不能修改它。

+1 用于清晰度和文档参考。还有两个问题:为什么要推迟数组?那是做什么的?此外,Perl OOP比我用PHP习惯的更抽象,所以你说的“go full bore”具体是什么意思?在PHP中,我会按照以下过程进行:http://pastebin.com/iZ1gUfAm - djthoms
@djthoms 引用只是对象的内存位置。例如,我的 $self->{OPTION} 只是指向我存储数组的内存位置。因此 ARRAY(0x1663b78) 表示你正在将一个数组存储在内存位置 0x1663b78。你想要的不是内存位置,而是那个内存位置上的数组。引用接受一个数组并给你一个内存位置。解引用获取内存位置并返回数组。 - David W.
@djthoms 我在我的答案中添加了如何编写Foo包的全套包。我的方法返回值而不是打印它们。这是为我的程序所做的。我类的各个部分对整个类的结构知之甚少。我的构造函数不知道我如何存储选项。我的 Option 方法不知道我如何存储 Distro。将每个部分分开并具有单一目的使编写类更清洁、更易于维护。 - David W.
Perl对象没有真正的私有方法。有一个社区协议,你不使用未记录的方法,也不直接操作结构,就像有一个约定俗成的社会契约,你不偷窥开着窗户的房子,因为有人忘记拉窗帘。通常,如果某个方法以下划线开头,就被视为私有的。然而,你应该使用POD来记录你的类。如果某些东西没有被记录,它就是私有的。 - David W.
这是一个真正棒极了的答案。我的唯一批评是 $self->{OPTION} = [] unless exists $self->{OPTION} 是多余的,因为存在自动创建。 - amon
非常详细的回答!Perl绝对是与其他脚本语言不同的学习曲线。感谢您提供所有这些非常有用的信息! - djthoms

4

$self->{options} 不是数组,而是一个指向数组的 引用。你需要对它进行解引用才能遍历其值。

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

1
您需要进行以下更改。
foreach(@{$self->{options}}) {
        ^^                ^

"options是一个arrayref的关键字,你需要对其进行解引用才能使用它。
至于你的另一个问题,建议使用更通用的一对pragma,特别是对于初学者。"
use strict;
use warnings;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接