Vraag Waarom zou ik een hash- of hash-referentie in Perl retourneren?


Wat is de meest effectieve manier om de onderstaande te bereiken? (Ik weet dat ze hetzelfde bereiken, maar hoe zouden de meeste mensen dit tussen de drie doen, en waarom?)

Bestand a.pl

my %hash = build_hash();
# Do stuff with hash using $hash{$key}
sub build_hash
{
    # Build some hash
    my %hash = ();
    my @k = qw(hi bi no th xc ul 8e r);
    for ( @k )
    {
        $hash{$k} = 1;
    }

    # Does this return a copy of the hash??
    return %hash;
}

Bestand b.pl

my $hashref = build_hash();
# Do stuff with hash using $hashref->{$key}
sub build_hash
{
    # Build some hash
    my %hash = ();
    my @k = qw(hi bi no th xc ul 8e r);
    for ( @k )
    {
        $hash{$k} = 1;
    }

    # Just return a reference (smaller than making a copy?)
    return \%hash;
}

Bestand c.pl

my %hash = %{build_hash()};
# Do stuff with hash using $hash{$key}
# It is better, because now we don't have to dereference our hashref each time using ->?

sub build_hash
{
    # Build some hash
    my %hash = ();
    my @k = qw(hi bi no th xc ul 8e r);
    for ( @k )
    {
        $hash{$k} = 1;
    }

    return \%hash;
}

10
2018-01-05 23:06


oorsprong


antwoorden:


Ik geef er de voorkeur aan om een ​​hash-ref te retourneren om twee redenen. Ten eerste, het gebruikt een beetje minder geheugen omdat er geen kopie is. Twee, het laat je dit doen als je gewoon een stuk hasj nodig hebt.

my $value = build_hash()->{$key};

Leer van hash-referenties houden, je zult ze veel zien zodra je objecten gaat gebruiken.


22
2018-01-06 00:50



Waarom niet beide terugsturen? Context is een zeer krachtige functie in Perl om uw functies toe te staan ​​"te doen wat u bedoelt". Vaak is de beslissing waarvan een betere retourwaarde is afhankelijk van hoe de aanroepcode van plan is om de waarde te gebruiken. Precies daarom heeft Perl de ingebouwde waarde. wantarray.

sub build_hash {
    my %hash;
    @hash{@keys} = (1) x @keys;
    wantarray ? %hash : \%hash
}

my %hash = build_hash;  # list context, a list of (key => value) pairs
my $href = build_hash;  # scalar context, a hash reference

9
2018-01-06 07:39



Ik zou de verwijzing retourneren om de verwerkingstijd van het afvlakken van de hash op te slaan in een lijst met scalar's, de nieuwe hash en (mogelijk) vuilnis te verzamelen om de lokale hash in de subroutine te verzamelen.


8
2018-01-05 23:17



Waar je naar op zoek bent, is een hash slice:

# assigns the value 1 to every element of the hash

my %hash;                                   # declare an empty hash
my @list = qw(hi bi no th xc ul 8e r);      # declare the keys as a list
@hash{@list} =                              # for every key listed in @list,
                (1) x @list;                # ...assign to it the corresponding value in this list
                                            # which is (1, 1, 1, 1, 1...)  (@list in scalar context
                                            #   gives the number of elements in the list)

De x operator wordt beschreven op perldoc perlop.

Zien perldoc perldsc en perldoc perlchtut voor tutorials over datastructuren en referenties (zowel must-reads voor zowel beginners als gevorderden). Hash plakjes zelf worden genoemd in perldoc perldata.

Als u een hash uit een functie retourneert, moet u normaal gesproken de hash zelf retourneren, geen verwijzing. Je zou een referentie kunnen gebruiken als de hash is reusachtig en geheugen of tijd is een zorg, maar dat zou niet je eerste zorg moeten zijn - het krijgen van de code werkt.

Retourwaarden van functies zijn altijd lijsten (waarbij het retourneren van een scalair in feite een lijst van één element is). Hashes zijn lijsten in Perl: U kunt onderling verwisselbaar toewijzen (ervan uitgaande dat de lijst een even aantal elementen bevat en er geen key collisions zijn die ertoe zouden leiden dat sommige waarden tijdens de conversie verloren gaan):

use strict; use warnings;
use Data::Dumper;

function foo
{
    return qw(key1 value1 key2 value2);
}

my @list = foo();
my %hash = foo();

print Dumper(\@list);
print Dumper(\%hash);

geeft:

$VAR1 = [
          'key1',
          'value1',
          'key2',
          'value2'
        ];

$VAR1 = {
          'key2' => 'value2',
          'key1' => 'value1'
        };

PS. Ik beveel ten zeerste aan om kleine voorbeeldprogramma's zoals die hierboven op te nemen om met datastructuren te spelen en om te zien wat er gebeurt. Je kunt veel leren door te experimenteren!


5
2018-01-05 23:17



a.pl en c.pl vereisen dat een kopie van de hash wordt genomen (en de hash intern voor de functie wordt gemarkeerd als vrij geheugen). b.pl, aan de andere kant, bouwt de hash slechts eenmaal en vereist weinig extra geheugen om een ​​referentie terug te geven waarop je kunt werken. Dus b.pl is waarschijnlijker de meest efficiënte vorm van de drie, zowel in ruimte als in tijd.


2
2018-01-05 23:41



Ik ga tegen de stroom in en wat iedereen zegt, en zeg dat ik er de voorkeur aan geef mijn gegevens als een hash te retourneren (nou ja, als een even grote lijst die waarschijnlijk wordt geïnterpreteerd als een hash). Ik werk in een omgeving waar we de neiging hebben om dingen te doen zoals het volgende codefragment, en het is veel gemakkelijker om te combineren en sorteren en slicen en dobbelen wanneer je niet elke andere regel hoeft te dereferen. (Het is ook leuk om te weten dat iemand je hashref niet kan beschadigen omdat je het hele ding op waarde hebt overgeschreven. Bewerk: tenzij je referenties hebt naar andere objecten / hashes / arrays in de hash-waarden, dan zit je sowieso in de problemen).

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

Dit benadert iets dat mijn code zou kunnen doen: een configuratie voor een object bouwen op basis van verschillende configuratiestrategieobjecten (waarvan sommige het object inherent kent, plus een extra persoon) en vervolgens enkele van deze als irrelevant filteren.

(Ja, we hebben leuke tools zoals hashgrep en hashmap en lkeys die nuttige dingen doen met hashes. $ a en $ b krijgen respectievelijk de sleutel en de waarde van elk item in de lijst). (Ja, we hebben mensen die op dit niveau kunnen programmeren. Het inhuren is onaangenaam, maar we hebben een kwaliteitsproduct.)

Als je niet van plan bent om iets als dit te doen dat lijkt op functioneel programmeren, of als je meer prestaties nodig hebt (heb je geprofileerd?) Dan gebruik je zeker hashrefs.


2
2018-01-06 04:22



Betreffende het retourneren van een hash van een   functie, normaal zou je moeten terugkeren   de hash zelf, geen referentie. U   zou een referentie kunnen gebruiken als de hash is   enorm en geheugen of tijd is een zorg,   maar dat zou niet je eerste zorg moeten zijn   - het krijgen van de code werkt.

Ik ga het hier niet eens zijn met Ether. Er was een tijd dat ik die positie innam, maar al snel merkte ik dat ik afdaalde in een hel dat ik me moest herinneren welke subs teruggekeerde hashes en die hashrefs terugstuurden, wat een nogal serieuze belemmering was om de code gewoon te laten werken. Het is belangrijk om beide te standaardiseren altijd het retourneren van een hash / array of altijd een hashref / arrayref retourneren, tenzij je constant over jezelf wilt struikelen.

Voor wat betreft standaardiseren zie ik verschillende voordelen van referenties:

  • Wanneer u een hash of array retourneert, is wat u feitelijk retourneert een lijst met een afgevlakte kopie van de originele hash / array. Net zoals het doorgeven van hash / array-parameters naar a sub, dit heeft als nadeel dat je maar één lijst per keer kunt verzenden. Toegegeven, je hoeft niet vaak meerdere waardenlijsten te retourneren, maar het gebeurt wel, dus waarom kiezen voor standaardiseren om dingen te doen op een manier die dit uitsluit?

  • De (meestal verwaarloosbare) prestaties / geheugenvoordelen van het retourneren van een enkele scalaire variabele in plaats van een potentieel veel groter aantal gegevens.

  • Het onderhoudt consistentie met OO-code, die vaak objecten (d.w.z. gezegende verwijzingen) heen en weer doorgeeft.

  • Als, om welke reden dan ook, het belangrijk is dat u een nieuwe kopie van de hash / array hebt in plaats van een verwijzing naar het origineel, kan de aanroepcode gemakkelijk een maken, zoals het OP aantoonde in c.pl. Als u echter een kopie van de hash retourneert, kan de beller op geen enkele manier een verwijzing naar het origineel maken. (In gevallen waar dit voordelig is, kan de functie een kopie maken en een verwijzing naar de kopie retourneren, waardoor het origineel wordt beschermd en tevens de "deze geeft hashes terug, dat geeft hashrefs terug "de hel die ik eerder noemde.)

  • Zoals Schwern al zei, het is echt leuk om te kunnen doen my $foo = $obj->some_data->{key}.

Het enige voordeel dat ik kan zien altijd terugkerende hashes / arrays is dat het gemakkelijker is voor diegenen die geen referenties begrijpen of zich niet op hun gemak voelen bij het werken met hen. Aangezien comfort met referenties een kwestie van weken of maanden duurt om zich te ontwikkelen, gevolgd door jaren of decennia van vloeiend samenwerken met hen, beschouw ik dit geen zinvol voordeel.


2
2018-01-06 13:40