【问题标题】:Should I choose a hash, an object or an array to represent a data instance in Perl?我应该选择散列、对象还是数组来表示 Perl 中的数据实例?
【发布时间】:2011-04-19 05:24:26
【问题描述】:

我一直对此感到疑惑,但从未真正深入研究过。

情况是这样的:我有一组比较大的数据实例。每个实例具有相同的集合或属性,例如:

# a child instance
name
age
height
weight
hair_color
favorite_color
list_of_hobbies

通常我会将一个孩子表示为一个散列,并将所有孩子放在一个散列散列(或散列数组)中。

这种方法一直困扰着我的是,我并没有真正使用所有孩子(内部哈希)都具有相同结构的事实。如果数据真的很大,似乎在内存方面可能会浪费,所以如果每个内部哈希都是从头开始存储的,那么 key names 的名称似乎比数据占用更多的空间本身... 另请注意,当我构建此类数据结构时,我经常将它们nstore 写入磁盘。

我想知道创建一个子 object 从这个角度来看是否更有意义,即使我并不真的需要 OO。会不会更紧凑?查询会更快吗?

或者也许将每个孩子表示为一个数组有意义?例如:

my ($name, $age, $height, $weight, $hair_color, $favorite_color, $list_of_hobbies) = 0..7; 
my $children_h = {
  James => ["James", 12, 1.62, 73, "dark brown", "blue", ["playing football", "eating ice-cream"]], 
  Norah => [...], 
  Billy => [...]
};
print "James height is $children_h->{James}[$height]\n";

回想一下,我主要关心的是空间效率(存储时的 RAM 或磁盘)、时间效率(即加载存储的数据集然后从实例 y 中获取属性 x 的值)和...方便性(代码可读性等) .

谢谢!

【问题讨论】:

    标签: arrays perl data-structures hash object


    【解决方案1】:

    我想这主要是个人喜好(当然,除非其他人也必须处理您的代码) 无论如何,我认为你应该看看moose 这绝对不是最节省时间和空间的,但它是最愉快和最安全的工作方式。 (通过安全,我的意思是使用您的对象的其他人不能轻易滥用它)

    当我真正代表某物时,我个人更喜欢一个对象。 当我在 perl 中处理对象时,我更喜欢moose

    格, ldx

    【讨论】:

      【解决方案2】:
      1. Perl 足够聪明,可以在哈希之间共享密钥。如果您有 100,000 个具有相同五个键的散列,则 perl 将这五个字符串存储一次,并对其进行十万次引用。担心空间效率不值得你花时间。

      2. 基于哈希的对象是最常见的对象,也是最容易使用的对象,因此除非有充分的理由不使用,否则应该使用它们。

      3. 1234563以其他方式制作它们 - 再一次,你不应该在没有充分理由的情况下这样做。)

      【讨论】:

      • +1 感谢您提供简洁、翔实的答案。很高兴知道 Perl 在哈希之间共享密钥。我也听从了你(和 daotoad)的建议,开始使用 Moose,结果证明它非常好。
      【解决方案3】:

      每当我尝试在使用散列或数组来存储数据之间做出决定时,我几乎总是使用散列。我几乎总能找到一种有用的方法来索引列表中的值以便快速查找。但是,您的问题更多是关于数组引用的哈希与哈希引用的哈希与对象引用的哈希。

      在上面的示例中,我会使用哈希引用的哈希而不是数组引用的哈希。我唯一会使用数组的时候是当数据中存在应该维护的固有顺序时,这样我就可以按顺序查找。在这种情况下,您存储的数组中实际上并没有任何固有顺序(例如,您在体重之前任意选择了身高),因此(以我的拙见)将数据存储为哈希值会更合适键是您存储的数据的描述(姓名、身高、体重等)。

      至于你应该使用哈希引用的哈希还是对象引用的哈希,这通常是一个偏好问题。面向对象的 Perl 有一些开销,所以我尝试只在我可以在可用性方面获得很大好处时才使用它。我通常只在存在与数据固有关联的操作时才使用对象/类(所以我可以写$my_obj->fix(); 而不是fix($my_obj);)。如果您只是存储数据,我会说坚持使用哈希。

      在 RAM 使用或从磁盘读取/写入磁盘的时间上不应有显着差异。在可读性方面,我认为使用散列而不是数组将获得巨大的好处,因为使用散列,键实际上是有意义的,但数组只是由与数据没有真正关系的数字索引。如果您以纯文本形式存储,这可能需要更多磁盘空间来存储,但如果这是一个大问题,您可以随时压缩数据!

      【讨论】:

        【解决方案4】:

        我通常从哈希开始并对其进行操作,直到找到我真正想要的数据派生来自我拥有的数据的实例。和/或我想要某种特殊的——甚至是多态的——行为。

        此时,我开始创建一个包来存储类行为,并根据需要实现方法。

        另一种情况是,我认为这些数据会在多个实例中有用。在这种情况下,它要么在你认为需要它的任何地方重写所有选择案例,要么将行为打包到一个类中,这样你就不必太多复制或研究下次您想使用该数据时的案例。

        【讨论】:

          【解决方案5】:

          一般来说,如果您不需要绝对的效率,散列将是您的最佳选择。在 Perl 中,对象只是一个带有类名的$something。对象可以是散列、数组、标量、代码引用,甚至是内部的全局引用。所以对象只能是方便而不是效率的胜利。

          如果您想试一试数组,使其有点可维护的典型方法是使用字段名称的常量:

          use strict;
          use warnings;
          use constant {
            NAME            => 0,
            AGE             => 1,
            HEIGHT          => 2,
            WEIGHT          => 3,
            HAIR_COLOR      => 4,
            FAVORITE_COLOR  => 5,
            LIST_OF_HOBBIES => 6,
          };
          
          my $struct = ["James", 12, 1.62, 73, "dark brown", "blue", ["playing football", "eating ice-cream"]];
          
          # And then access it with the constants as index:
          print $struct->[NAME], "\n";
          $struct->[AGE]++; # happy birthday!
          

          或者,您可以尝试如下使用数组(对象)是否更有意义:

          package MyStruct;
          use strict;
          use warnings;
          use Class::XSAccessor::Array
            accessors => {
              name => 0,
              age => 1,
              height => 2,
              weight => 3,
              hair_color => 4,
              favorite_color => 5,
              list_of_hobbies => 6,
            };
          
          sub new {
            my $class = shift;
            return bless([@_] => $class);
          }
          
          package main;
          my $s = MyStruct->new;
          $s->name("James");
          $s->age(12);
          $s->height(1.62);
          $s->weight(73);
          # ... you get the drill, but take care: The following is fine:
          $s->list_of_hobbies(["foo", "bar"]);
          # This can produce action-at-a-distance:
          my $hobbies = ["foo", "bar"];
          $s->list_of_hobbies($hobbies);
          $hobbies->[1] = "baz"; # $s changed, too (due to reference)
          

          回到我原来的观点:通常,您需要散列或基于散列的对象。

          【讨论】:

          • 谢谢你,这很有教育意义。
          【解决方案6】:

          除非需要绝对速度调整,否则我会使用 Moose 制作对象。为了纯粹的速度,使用常量索引和数组。

          我喜欢对象,因为它们减少了处理大型深层结构所需的脑力劳动。例如,如果您构建一个数据结构来表示学校中的各个教室。你会得到一个孩子名单、一个老师和一个房间号之类的东西。如果您将所有东西都放在一个大结构中,那么您必须知道该结构的内部结构可以访问教室中孩子们的爱好。使用对象,您可以执行以下操作:

          my @all_hobbies = uniq map $_->all_hobbies, 
                                 map $_->all_students, $school->all_classrooms;
          

          我不关心内部结构。我可以简洁地生成所有孩子爱好的唯一列表。所有复杂的访问仍在发生,但我不需要担心发生了什么。我可以简单地使用界面。

          这是您的子班的 Moose 版本。我设置了 hobbies 属性以使用数组 trait,所以我们得到了一堆简单的方法来询问。

          package Child;
          
          use Moose;
          
          has [ 'name', 'hair_color', 'fav_color' ] => (
              is => 'ro',
              isa => 'Str',
              required => 1,
          );
          
          has [ 'age', 'height', 'weight' ] => (
              is => 'ro',
              isa => 'Num',
              required => 1,
          );
          
          has hobbies => (
              is      => 'ro',
              isa     => 'Int',
              default => sub {[]},
              traits  => ['Array'],
              handles   => {
                  has_no_hobbies => 'is_empty',
                  num_hobbies    => 'count',
                  has_hobbies    => 'count',
                  add_hobby      => 'push',
                  clear_hobbies  => 'clear',
                  all_hobbies    => 'elements',
              },
          );
          
          # Good to do these, see moose best practices manual.
          __PACKAGE__->meta->make_immutable;
          no Moose;
          

          现在使用 Child 类:

          use List::MoreUtils qw( zip );
          
          # Bit of messing about to make array based child data into objects;
          
          @attributes = qw( name age height weight hair_color fav_color hobbies );
          
          my @children = map Child->new( %$_ ),
                         map { zip @attributes, @$_ }, 
              ["James", 12, 1.62, 73, "dark brown", "blue",  ["playing football", "eating ice-cream"]], 
              ["Norah", 13, 1.75, 81, "black",      "red",   ["computer programming"]],
              ["Billy", 11, 1.31, 63, "red",        "green", ["reading", "drawing"]], 
          ;
          
          
          # Now index by name:
          
          my %children_by_name = map { $_->name, $_ } @children;
          
          # Here we get kids with hobbies and print them.
          
          for my $c ( grep $_->has_hobbies, @children ) {
              my $n = $c->name;
              my $h = join ", ", $c->all_hobbies;
              print "$n likes $h\n";
          }
          

          【讨论】:

          • +1 感谢 Moose 101,daotoad。我已经开始使用了,真的很好玩!
          猜你喜欢
          • 2016-04-10
          • 1970-01-01
          • 1970-01-01
          • 2018-07-23
          • 1970-01-01
          • 1970-01-01
          • 2012-01-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多