如何使用子程序在复杂的数据结构上全局工作?

时间:2013-11-28 15:02:40

标签: perl

具体来说,我的数据结构如下所示

{
  "SomeGuy" => {
    date_and_time => "11-04-2013",
    Id => 7,
    nr => 52,
    picks => [
      { "This is an option" => "Option3" },
      { "This is another option" => "Option4" },
      { "This is another option" => "Option1" },
      { "And another one" => "Something" },
    ],
    time_of => "06:11 AM",
    total => 1,
  },
  "Another Guy" => { ... },
}

这是通过Data::Dump输出的。实际的数据结构包含更多的记录,如"SomeGuy"。所有这些结构都相同。

我用这种方式填充这个数据结构:

$guys->{$profile}{options}[$total++]{$var} = $var2;
$guys->{$profile}{Id} = $i;
$guys->{$profile}{date_and_time} = get_date($Time[0]);
$guys->{$profile}{time_of} = $Time[1];
$guys->{$profile}{total} = keys (% {$guys->{$profile}{options}[0]});
$guys->{$profile}{nr} = $pNr;

拥有所有这些,我接下来要做的就是对这个数据结构进行操作。我再说一遍,数据结构中有很多记录。

当我输出它的内容时,我会以混乱的顺序得到它,也就是说,不是按照它的填充顺序。我已经尝试使用Data::DumperData::Dump并亲自遍历记录。

我知道Data命名空间中的方法因此而臭名昭着,这就是为什么Data::Dumper提供了一种对子例程进行排序的方法,而Data::Dump提供了一个默认的方法。

所以我有数据结构。它看起来像我期望的那样,我拥有所有数据,因为我知道它应该看起来有效。我想根据他们的Id字段对记录进行排序。我的想法是,我必须使用子程序,并基本上将数据结构的引用传递给它,并在那里进行排序。

 sub sortt {
    my $dref = shift @_;
    foreach my $name ( sort { $dref->{$a}{Id} <=> $dref->{$b}{Id} } keys %$dref ) {
      print "$data->{$name}{Id}: $name \n";
    }
 }

调用它(在填充结构的同一范围内,所以不用担心):

 sortt(\$guys);

错误是:

 Not a HASH reference at perlprogram.pl line 452

所以我去子程序中使用ref以确保我传递了一个实际的引用。它说REF

接下来,我进入绝望的模式并尝试一些愚蠢的事情,比如用它来调用:

sortt(\%$guys)

但是,如果我没有误会,只需将副本发送到子程序,然后在本地对该副本进行排序,那就不用了。

如果我复制并从子例程返回它是没有用的,我只想传递我的数据结构的引用并对其进行排序,并让它全局(或在调用范围本身)中反映这些更改。我该怎么做?

5 个答案:

答案 0 :(得分:4)

除了“Not a ref”的语法问题外,您首先要从错误的一端接近问题。我会将小句法细节留给其他人(见池上的评论)。

您根本无法对它们进行排序,因为$guys哈希,而不是数组。哈希是 NOT 在Perl中排序的。如果你想对它进行排序,你有三个解决方案:

  1. 将有序的名称列表存储为单独的数组。

    my @ordered_names = sort { $guys->{$a}{Id} <=> $guys->{$b}{Id} } keys %$guys

    然后使用数组进行排序,并返回到各个记录的hashref。

  2. 将名称添加到单个人的哈希值,而不是外部哈希引用。 $guys应该是数组引用。这种方法的缺点是你不能再按照他们的名字查找一个人的记录了 - 如果需要这个功能,请使用#1。

    @codnodder的答案显示了如何做到这一点,如果你不关心按名称访问记录。

  3. 使用Tie::*模块。 (Tie::IxHashTie::Hash::Sorted)。不推荐,因为速度较慢。

答案 1 :(得分:1)

Perl哈希本质上是无序的。您无法对它们进行排序,也无法对它们进行重新排序。您必须编写代码以按所需顺序访问元素。

您的sortt子例程除了打印ID和每个哈希元素的名称(按ID排序)外什么都不做。除非它没有,因为您在实际设置$data时尝试使用变量$dref。这可能是导致Not a HASH reference错误的原因,但除非您显示整个代码,或者至少指出哪个是perlprogram.pl line 452,否则我们无法帮助您。{/ p>

执行所需操作的最佳方法是创建散列键的数组,您可以按照所需的顺序对其进行排序。喜欢这个

my @names_by_id = sort { $guys->{$a}{Id} <=> $guys->{$b}{Id} } keys %$guys;

然后您可以使用它来访问排序顺序中的哈希值,就像这样,打印出与您的sortt相同的输出,但格式化得更好

for my $name (@names_by_id) {
  printf "%4d: %s\n", $guys->{$name}{Id}, $name;
}

如果你想按排序顺序对哈希元素做任何其他事情,那么你必须使用这种技术。

答案 2 :(得分:0)

$guys已经是哈希引用,因此您只需要sortt($guys)

如果你想要一个排序的数据结构,你需要这样的东西:

my @guys_sorted =
    map  { { $_ => $guys->{$_} } }
    sort { $guys->{$a}{Id} <=> $guys->{$b}{Id} } keys %$guys;

print(Dumper(\@guys_sorted));

或者,在子目录中:

sub sortt {
    # returns a SORTED ARRAY of HASHREFS
    my $ref = shift;
    return map  { { $_ => $ref->{$_} } }
           sort { $ref->{$a}{Id} <=> $ref->{$b}{Id} } keys %$ref;
}

print(Dumper([sortt($guys)]));

答案 3 :(得分:0)

这是一个非常复杂的数据结构。如果您在程序中通常使用这样的结构,我建议您将Perl编程技能提升一个档次,并研究一下Object Oriented Perl的学习。

面向对象的Perl相当简单:您的对象仅仅是您之前创建的数据结构。方法只是与该对象一起使用的子例程。大多数方法都是getter / setter方法来设置你的结构。

最初,它有点多写,但是一旦掌握了它,额外的写入很容易通过保存调试和维护代码来补偿。

面向对象的Perl做了两件事:它首先确保你的结构是正确的。例如:

$some_guy->{Picks}->[2]->{"this is an option"} = "Foo!";

糟糕!应该是{picks}。想象一下,试图在代码中找到错误。

在OO-Perl中,如果我输错了方法的名称,程序会立即将其取出:

$some_guy->picks(
    {
        "This is an option" -> "Foo!",
        "This is option 2"  => "Bar!",
    }
)

如果我有$some_guy->Picks,我会遇到运行时错误。

它还使您认为您的结构为对象。例如,你在排序什么?你正在对你的Guys的ID进行排序,这些人存储在名为%guys的哈希中。

# $a and $b are hash keys from `%guys`.
# $guys{$a} and $guys{$b} represent the guy objects.
# I can use the id method to get they guys' IDs
sort_guys_by_id {
    guys{$a}->id cmp guys{$b}->id;  #That was easy!
}

看看tutorial。你会发现自己编写的程序更好,错误更少。

答案 4 :(得分:0)

如果您的心设置在已排序的数据结构上,我建议您执行以下操作。它是一个简单的哈希数组,而不是使用name字符串作为单元素哈希的键,它为每个人的数据添加了一个新的name键。

我希望通过Data::Dump输出,它是不言自明的。它按您的请求按Id字段排序,但仍然的缺点是单独的索引数组将允许所有的所有排序修改或复制原始哈希数据。

use strict;
use warnings;

use Data::Dump;

my $guys = {
  "SomeGuy " => {
    date_and_time => "11-04-2013",
    Id => 7,
    nr => 52,
    picks => [
      { "This is an option" => "Option3" },
      { "This is another option" => "Option4" },
      { "This is another option" => "Option1" },
      { "And another one" => "Something" },
    ],
    time_of => "06:11 AM",
    total => 1,
  },
  "Another Guy" => { Id => 9, nr => 99, total => 6 },
  "My Guy" => { Id => 1, nr => 42, total => 3 },
};

my @guys_sorted = map {
  my $data = $guys->{$_};
  $data->{name} = $_;
  $data;
}
sort {
  $guys->{$a}{Id} <=> $guys->{$b}{Id}
} keys %$guys;

dd \@guys_sorted;

<强>输出

[
  { Id => 1, name => "My Guy", nr => 42, total => 3 },
  {
    date_and_time => "11-04-2013",
    Id => 7,
    name => "SomeGuy ",
    nr => 52,
    picks => [
      { "This is an option" => "Option3" },
      { "This is another option" => "Option4" },
      { "This is another option" => "Option1" },
      { "And another one" => "Something" },
    ],
    time_of => "06:11 AM",
    total => 1,
  },
  { Id => 9, name => "Another Guy", nr => 99, total => 6 },
]