Коя е най-добрата практика на Perl за връщане на хешове от функции?

Обмислям най-добра практика за предаване на хеш препратки за връщане на данни към/от функции.

От една страна, изглежда интуитивно да подадете само входни стойности към функция и да имате само връщани изходни променливи. Въпреки това, предаването на хешове в Perl може да се извърши само чрез препратка, така че е малко объркващо и изглежда по-скоро възможност за грешка.

Другият начин е да се предаде референция във входните променливи, но тогава трябва да се работи във функцията и може да не е ясно какво е входна и какво е върната променлива.

Каква е най-добрата практика по отношение на това?

Връща препратки към масив и хеш и след това го дереферира.

($ref_array,$ref_hash) = $this->getData('input');
@array = @{$ref_array};
%hash = %{$ref_hash};

Предайте препратки (@array, %hash) към функцията, която ще съдържа изходните данни.

$this->getData('input', \@array, \%hash);

person Dan Littlejohn    schedule 07.07.2009    source източник


Отговори (7)


Просто върнете препратката. Няма нужда да дереферирате целия хеш, както правите във вашите примери:

my $result = some_function_that_returns_a_hashref;
say "Foo is ", $result->{foo};
say $_, " => ", $result->{$_} for keys %$result;

и т.н.

Никога не съм виждал някой да предава празни препратки, за да задържи резултата. Това е Perl, не C.

person jrockway    schedule 07.07.2009
comment
Какво ще кажете за GetOpt::Std. Можете да подадете празна хеш препратка като втори аргумент на getopts(a:b:c:, \%hash_ref), за да попълните хеш препратката за достъп, т.е. $hash_ref{opt_a} и т.н. Не казвам, че е красиво, но Perl библиотеки са го приложили. - person Chris Serra; 11.03.2011

Опитвайки се да създадете копия, като кажете

my %hash = %{$ref_hash};

е дори по-опасно от използването на hashref. Това е така, защото създава само плитко копие. Това ще ви накара да мислите, че е добре да промените хеша, но ако съдържа препратки, те ще променят оригиналната структура на данните. Смятам, че е по-добре просто да предавате препратки и да внимавате, но ако наистина искате да сте сигурни, че имате копие от предадената препратка, можете да кажете:

use Storable qw/dclone/;

my %hash = %{dclone $ref_hash};
person Chas. Owens    schedule 07.07.2009
comment
Добро допълнение към дискусията. Мисля, че тази грешка е често срещана, защото бързият тест на нещо просто като 1-D масив изглежда работи. Тогава ppl се обърква, че не се простира до по-сложни структури като хешове и N-D масиви, N›1. - person SSilk; 06.09.2017

Първият е по-добър:

my ($ref_array,$ref_hash) = $this->getData('input');

Причините са:

  • във втория случай getData() трябва да провери структурите от данни, за да се увери, че са празни
  • имате свободата да върнете undef като специална стойност
  • изглежда по-идиоматично на Perl.

Забележка: линиите

@array = @{$ref_array};
%hash = %{$ref_hash};

са съмнителни, тъй като копирате повърхностно всички структури от данни тук. Можете да използвате препратки навсякъде, където имате нужда от масив/хеш, като използвате -> оператор за удобство.

person Igor Krivokon    schedule 07.07.2009
comment
Причината, поради която направих дереферирането в миналото, е да го направя по-четлив, така че да не се налага да се опитвате да разберете какъв вид реф. - person Dan Littlejohn; 08.07.2009
comment

В момента се боря със следните RCP команди на Eclipse:

  • org.eclipse.ui.edit.cut
  • org.eclipse.ui.edit.copy
  • org.eclipse.ui.edit.paste

Използвам ги като командни приноси в лентата с инструменти, но UIElements (елементите на лентата с инструменти) не се актуализират, когато състоянието „handled“ на тези команди се промени.

За тестване използвах механизъм за анкетиране, за да проверя дали състоянието на тези команди наистина се променя в зависимост от текущо фокусирания елемент и открих, че манипулаторът остава същият, но „обработеното“ състояние на манипулатора се променя правилно, което води до „обработване“ на командите състоянието също да се промени правилно.

Единственият проблем е, че нито една от тези промени в състоянието не предизвиква известие (нито на ICommandListener на командата, нито на IHandlerListener на манипулатора), така че UIElements няма да се актуализират.

Ето някои тестови кодове за наблюдение на състоянията на дадена команда:

ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);

final String commandId="org.eclipse.ui.edit.copy";
Command command = commandService.getCommand(commandId);
command.addCommandListener(new ICommandListener() {

    public void commandChanged (CommandEvent commandEvent) {
        System.out.println(">> Command changed: " + commandId);
    }
});

Пропускам ли нещо или това е грешка в реализациите на манипулатора за изрязване/копиране/поставяне? Някакви прозрения?

РЕДАКТИРАНЕ: Командите са активирани през цялото време и манипулаторът никога не се обменя, само състоянието 'handled' на манипулатора (и по този начин също и състоянието 'handled' на командата) се променя в зависимост от това кой елемент на потребителския интерфейс е на фокус. Няма обаче известие, когато това състояние се промени. Това води до това, че бутоните на лентата с инструменти винаги са активирани и натискането им ще доведе до org.eclipse.core.commands.NotHandledException: There is no handler to execute for command.

- person Massa; 08.07.2009

Ако става достатъчно сложно, че и сайтът за извикване, и извиканата функция плащат за това (защото трябва да мислите/пишете повече всеки път, когато го използвате), защо просто не използвате обект?

my $results = $this->getData('input');

$results->key_value_thingies;
$results->listy_thingies;

Ако създаването на обект е „твърде сложно“, тогава започнете да използвате Moose, така че вече да не е.

person Community    schedule 08.07.2009
comment
абе, обектите в perl винаги са били нещо като половинка. Идеята е добра, но опитът да се използва това за голям набор от данни за скорост не работи. - person Dan Littlejohn; 11.07.2009
comment
ако наистина смятате, че това е бавно, използвайте C++ вместо това. Perl е бавен поради много други причини, инстанцирането на обект не е една от тях. - person ; 14.07.2009

Моите лични предпочитания за подинтерфейси:

  1. Ако рутината има 0-3 аргумента, те могат да бъдат предадени под формата на списък: foo( 'a', 12, [1,2,3] );
  2. В противен случай подайте списък с двойки стойности на имена. foo( one => 'a', two => 12, three => [1,2,3] );
  3. Ако рутината има или може да има повече от един аргумент, сериозно обмислете използването на двойки име/стойност.

Предаването на препратки увеличава риска от непреднамерена промяна на данните.

При връщания обикновено предпочитам да връщам списък с резултати, а не масив или препратка към хеш.

Връщам референции за хеш или масив, когато това ще направи забележимо подобрение в скоростта или потреблението на памет (т.е. ГОЛЕМИ структури), или когато е включена сложна структура от данни.

Връщането на препратки, когато не са необходими, лишава човек от възможността да се възползва от хубавите характеристики на Perl за обработка на списъци и го излага на опасностите от неволно модифициране на данни.

По-специално намирам за полезно да присвоя списък с резултати в масив и да върна масива, което предоставя контекстното поведение на връщане на масив към моите подчинени.

В случай на предаване на два хеша бих направил нещо като:

my $foo = foo( hash1 => \%hash1, hash2 => \%hash2 ); # gets number of items returned
my @foo = foo( hash1 => \%hash1, hash2 => \%hash2 ); # gets items returned

sub foo {
   my %arg = @_;

   # do stuff

   return @results;
}
person daotoad    schedule 07.07.2009

Първоначално публикувах това към друг въпрос и след това някой посочи това като „свързана публикация“, така че ще го публикувам тук, за да си представя темата, ако приемем, че хората ще се сблъскат с нея в бъдеще.

Ще противореча на Приетия отговор и ще кажа, че предпочитам данните ми да бъдат върнати като обикновен хеш (добре, като списък с четен размер, който вероятно ще бъде интерпретиран като хеш). Работя в среда, в която сме склонни да правим неща като следния кодов фрагмент и е много по-лесно да комбинирате и сортирате, нарязвате и разделяте, когато не се налага да дереферирате всеки друг ред. (Също така е хубаво да знаете, че някой не може да повреди вашия hashref, защото сте предали цялото нещо по стойност - въпреки че някой посочи, че ако вашият хеш съдържа повече от прости скалари, това не е толкова просто.)

my %filtered_config_slice = 
   hashgrep { $a !~ /^apparent_/ && defined $b } (
   map { $_->build_config_slice(%some_params, some_other => 'param') } 
   ($self->partial_config_strategies, $other_config_strategy)
);

Това се доближава до нещо, което моят код може да направи: изграждане на конфигурация за обект, базиран на различни обекти на конфигурационна стратегия (някои от които обектът познава по същество, плюс някой допълнителен човек) и след това филтрира някои от тях като неподходящи.

(Да, имаме хубави инструменти като hashgrep и hashmap и lkeys, които правят полезни неща за хешовете. $a и $b се настройват съответно на ключа и стойността на всеки елемент в списъка). (Да, имаме хора, които могат да програмират на това ниво. Наемането е неприятно, но имаме качествен продукт.)

Ако не възнамерявате да правите нещо подобно на функционално програмиране като това или ако имате нужда от повече производителност (профилирали ли сте?), тогава със сигурност използвайте hashrefs.

person Community    schedule 06.01.2010

Ъъъ... "преминаването на хешовете може да се извърши само чрез препратка"?

sub foo(%) {
    my %hash = @_;
    do_stuff_with(%hash);
}

my %hash = (a => 1, b => 2);
foo(%hash);

какво ми липсва

Бих казал, че ако проблемът е, че трябва да имате няколко изхода от функция, по-добре е като обща практика да изведете структура от данни, вероятно хеш, която съдържа всичко, което трябва да изпратите, вместо да приемате модифицируеми препратки като аргументи .

person chaos    schedule 07.07.2009
comment
Почти съм сигурен, че когато го извикате по този начин, той изяжда всички аргументи след хеша и ги добавя към самия хеш. - person Eric; 08.07.2009
comment
Как това отговаря на въпроса? Може да е коментар, а не отговор. - person Igor Krivokon; 08.07.2009
comment
прототипите така или иначе са лоша практика. - person Dan Littlejohn; 08.07.2009
comment
@Eric: Не, прототипът (%) му дава специални правомощия. Ако сте го променили на sub foo(\%\@), можете да извикате foo(%hash, @array) и да извадите hashref и arrayref в рамките на подпрограмата. - person ephemient; 08.07.2009