Должен ли я выбрать хеш, объект или массив для представления экземпляра данных в Perl? - PullRequest
3 голосов
/ 30 сентября 2010

Мне всегда было интересно об этом, но я никогда не задумывался об этом.

Ситуация такая: у меня относительно большой набор экземпляров данных. Каждый экземпляр имеет одинаковый набор или свойства, например:

# a child instance
name
age
height
weight
hair_color
favorite_color
list_of_hobbies

Обычно я представлял бы ребенка в виде хэша, а всех детей собирал в хэш (или массив хэшей).

Что меня всегда беспокоило, так это то, что я не использую тот факт, что все дети (внутренние хэши) имеют одинаковую структуру. Кажется, что это может быть бесполезно с точки зрения памяти, если данные действительно большие, поэтому, если каждый внутренний хэш хранится с нуля, кажется, что имена имен ключей могут занять гораздо больше места, чем сами данные ... Также обратите внимание, что когда я строю такие структуры данных, я часто nstore записываю их на диск.

Интересно, имеет ли смысл создание дочернего объекта в этой перспективе, даже если мне не нужен ОО. Это будет более компактным? Будет ли быстрее запрашивать?

Или, возможно, имеет смысл представлять каждого ребенка в виде массива? e.g.:

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";

Вспомните мои основные проблемы: эффективность использования пространства (ОЗУ или диск при сохранении), эффективность по времени (т.е. загрузка сохраненного набора данных, получение значения свойства x из экземпляра y) и ... удобство (читаемость кода и т. Д.) .

Спасибо!

Ответы [ 6 ]

11 голосов
/ 30 сентября 2010
  1. Perl достаточно умен, чтобы делиться ключами между хэшами.Если у вас есть 100 000 хэшей с теми же пятью ключами, Perl сохраняет эти пять строк один раз и ссылается на них сто тысяч раз.Беспокойство об эффективности пространства не стоит вашего времени.

  2. Объекты на основе хеша - самый распространенный тип и с ними проще всего работать, поэтому вы должны использовать их, если у вас нет чертовски хорошегопричина не делать.

  3. Вы должны избавить себя от множества неприятностей, начать использовать Moose и перестать беспокоиться о внутренностях ваших объектов (хотя, только между вамии я, объекты Moose основаны на хэше, если вы не используете специальные расширения, чтобы сделать их иначе - и опять же, вы не должны делать это без веской причины.)

2 голосов
/ 30 сентября 2010

Если не требуется абсолютная настройка скорости, я бы сделал объект с помощью Moose. Для чистой скорости используйте постоянные индексы и массив.

Мне нравятся объекты, потому что они уменьшают умственные усилия, необходимые для работы с большими глубокими структурами. Например, если вы строите структуру данных для представления различных классных комнат в школе. У вас будет что-то вроде списка детей, учителя и номера комнаты. Если у вас все в большой структуре, вы должны знать, как внутренние структуры имеют доступ к увлечениям детей в классе. С объектами вы можете сделать что-то вроде:

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

Мне нет дела до внутренних органов. И я могу кратко составить уникальный список всех увлечений детей. Все сложные обращения все еще происходят, но мне не нужно беспокоиться о том, что происходит. Я могу просто использовать интерфейс.

Вот версия Moose вашего дочернего класса. Я настроил атрибут hobbies для использования свойства массива, поэтому мы получаем несколько методов просто для запроса.

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";
}
2 голосов
/ 30 сентября 2010

Я думаю, это в основном личный вкус (за исключением, конечно, когда другие люди тоже должны работать над вашим кодом) В любом случае, я думаю, вам стоит взглянуть на moose Это определенно не самый эффективный по времени и пространственному использованию, но это самый приятный и самый безопасный способ работы. (Под безопасностью я подразумеваю, что другие люди, которые используют ваш объект, не могут использовать его так же легко)

Я лично предпочитаю объект, когда я действительно представляю что-то. И когда я работаю с объектами в Perl, я предпочитаю лось

Gr, LDX

1 голос
/ 30 сентября 2010

Как правило, если вам не нужна полная эффективность, лучше всего использовать хэши.В 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)

Возвращаясь к моей исходной точке: обычно вам нужны хеши или объекты на основе хешей.

1 голос
/ 30 сентября 2010

Я обычно начинаю с хэша и манипулирую им, пока не найду случаи, когда действительно нужные мне данные получены из данных, которые у меня есть.И / или что мне нужно какое-то странное - или даже полиморфное - поведение.

С этого момента я начинаю создавать пакеты для хранения поведения классов, реализуя методы по мере необходимости.

Другой случай, когда я думаю, что эти данные будут полезны более чем в одном случае.В этом случае это либо переписывает все случаи выбора везде, где вы думаете, что вам это понадобится, либо упаковываете поведение в классе, чтобы вам не пришлось слишком много копировать или изучатьв следующий раз, когда вы захотите использовать эти данные.

0 голосов
/ 30 сентября 2010

Всякий раз, когда я пытаюсь решить, использовать ли хеш или массив для хранения данных, я почти всегда использую хеш. Я почти всегда могу найти полезный способ индексировать значения в списке для быстрого поиска. Тем не менее, ваш вопрос больше касается хэшей ссылок на массивы, хэшей ссылок на хэши и хешей ссылок на объекты.

В приведенном выше примере я бы использовал хэш ссылок на хэш, а не хэш ссылок на массивы. Единственный раз, когда я бы использовал массив, это когда в данных есть свойственный порядок, который нужно поддерживать, чтобы я мог искать вещи по порядку. В этом случае в массивах, которые вы храните, нет никакого внутреннего порядка (например, вы произвольно выбрали рост перед весом), поэтому было бы более уместно (по моему скромному мнению) хранить данные в виде хэша, где ключи - это описание данных, которые вы храните (имя, рост, вес и т. д.).

Что касается того, следует ли использовать хэш ссылок на объекты или хэш объектов, это часто может быть вопросом предпочтения. С объектно-ориентированным Perl связаны некоторые накладные расходы, поэтому я стараюсь использовать его только тогда, когда могу получить большую выгоду, скажем, в удобстве использования. Обычно я использую объекты / классы только тогда, когда есть действия, связанные с данными (поэтому я могу написать $my_obj->fix(); вместо fix($my_obj);). Если вы просто храните данные, я бы сказал, придерживайтесь хеша.

Не должно быть значительной разницы в использовании ОЗУ или во времени для чтения / записи на диск. С точки зрения читабельности, я думаю, вы получите огромное преимущество, используя хэши над массивами, поскольку с хэшами ключи действительно имеют смысл, но массивы просто индексируются числами, которые не имеют реальной связи с данными. Это может потребовать больше дискового пространства для хранения, если вы храните в виде простого текста, но если это огромная проблема, вы всегда можете сжать данные!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...