Perl: Return hash from subroutine

后端 未结 3 881
北荒
北荒 2020-12-10 18:19

I have been trying examples for hours but I can\'t seem to grasp how to do what I want to do.

I want to return a hash from a subroutine, and I figured a reference wa

3条回答
  •  一整个雨季
    2020-12-10 19:11

    To be clear, Perl works with scalars or lists of them:

    $scalar = 1;
    @list = ( $scalar, $scalar, $scalar );
    

    Each item of list may be accessed by index, e.g. $list[1].
    You can also access items by name. This structure is called a hash: $hash{ name1 }

    %hash = ( 'name1', $scalar, 'name2', $scalar, 'name3', $scalar )
    

    But, as you can see, this is still a list. Notice the "()" around it.
    And again, each item of the list can be only a scalar.

    I have not seen this in any book, but the $ sign means one value and @ means list of values.

    In this example you have one value, so you use the $ sign:

    $scalar =  $hash{ name1 };
    $scalar =  $list[ 1 ];
    

    In this next example you have a list of values, so you use "@":

    @list2 =  @list1;            # copy all items
    @list2 =  @list[ 1, 3..5 ];  # copy four items with index 1,3,4,5
    @list2 =  @hash{ 'name1', 'name3' }; #copy two items with index 'name1', 'name2'
    

    Perl has references. This is powerful tool.

    $ref = \$scalar;
    $ref = \@list;
    $ref = \%hash;
    

    $ref is also scalar, because it has only one value. To access to underlying data referred by this $ref, you should use a dereference.

    $scalar = $$ref;
    @list   = @$ref;
    %hash   = %$ref;
    

    But actually, you do not want the whole list or hash. You just want some item in them. For this, you use -> and either [] to tell Perl you want to access a list element, or {} to tell Perl you want to access a hash element:

    $scalar = $ref->[ 1 ];
    $scalar = $ref->{ name1 };
    

    NOTICE: you're accessing one element, so you use the $ sign.

    If you want list of elements from array or hashes, you use the @ sign. For example:

    @list =  @$ref[ 1, 3..5 ];
    @list =  @$ref{ 'name1', 'name2' };
    

    1st: $ref - returns reference to structure. $ says you get one value from variable 'ref'
    2nd: @$ref - you dereference $ref. @ says that you want to access the list of items by that reference.
    3rd-a: you get '1,3,4,5' items from array (NOTICE: [])
    3rd-b: you get 'name1', 'name2' items from hash (NOTICE: {})

    But when you a get reference to hash or list and put this reference to another hash or array, we may create complex structures such as an array of hashes of hashes, or hash of arrays of hashes. Examples:

    @list = ( 1, 2, 3, 4, 5 );
    %hash = ( 'a', 1, b => 2 );
    @list2 = ( \@list, \%hash, 3, 'y' );
    %hash2 = ( name1 => \@list2, d => 4 );
    %hash2 = ( 'name1', \@list2, 'd', 4 );  #same. no any difference.
    $href  = \%hash2;
    

    => - just quote left operand and put , after it.

    If you want to access to one item of 'hash2':

    $scalar =  $hash2{ name1 };
    $scalar =  $href->{ name1 };
    

    Using $href-> after dereferencing will mean %hash2.

    If you want to access to two or more items of 'hash2':

    @list =  @hash2{ 'name1', 'd' };
    @list =  @$href{ 'name1', 'd' };
    

    Using @$href after dereferencing will mean %hash2

    IN DETAIL:

    $scalar =  $hash2{ name1 }; # <--- What does this mean???
    

    $hash2 means we access one item of %hash2. Which is a reference to a list:

    $list_ref =  $hash2{ name1 };
    $scalar   =  $list_ref->[ 1 ]; # <--- what we get here???
    

    $list_ref means we access one item. ->[ means we access the list. Because $list_ref refers to @list2, we access \%hash. We can complete that in one step:

    $scalar =  $hash2{ name1 }->[ 1 ];
    

    You may think here as you replace the text '$list_ref' by '$hash2{ name1 }'

    We say that [ 1 ] refers to %hash. So to access one item of that hash we use $ again:

    $hash_ref  =  $hash2{ name1 }->[ 1 ];
    $scalar    =  $hash_ref->{ b };
    

    $hash_ref means we access one item. ->{ means we access to hash. Because $hash_ref refers to %hash we access 2. We can complete that in one step:

    $scalar =  $hash2{ name1 }->[ 1 ]->{ b };
    

    You again may think here as you replace text '$hash_ref' by '$hash2{ name1 }->[ 1 ]'. But hash2 here is %hash2. What about $href? Please remember this example:

    $scalar =  $hash2{ name1 };
    $scalar =  $href->{ name1 };
    

    You may notice, if you access the item by ref you just add ->. Compare:

    @l = ( 1, 2, 3, 4 );
    $scalar = $l[ 1 ];    # to access to second item of @l list
    $hr = \@l;
    $scalar = $hl->[ 1 ]; # to access to second item of @l list
    
    %h = @l;
    $scalar =  $h{ 1 }; 
    $hr =  \%h;
    $scalar =  $hr->{ 1 };
    

    The type of bracket after -> will be [ for an array or { for a hash item.

    What about $href?

    $scalar =  $hash2{ name1 }->[ 1 ]->{ b };
    $scalar =  $href->{ name1 }->[ 1 ]->{ b };
    

    After the first dereference we do not require ->

    $scalar =  $hash2{ name1 }[ 1 ]{ b };
                     ^-- first dereference
    $scalar =  $href->{ name1 }[ 1 ]{ b };
                    ^--first dereference
    

    Returning to your question: in Perl, you may pass a LIST of values to subs and return a LIST also.

    sub test {
        return @_;
    }
    

    Here we return all items we get.

    return \%hash;  # fn()->{ name1 }; # actually all these is list of one item
    return \@list;  # fn()->[ 1 ]; # so we may write: (fn())[0]->[ 1 ];
    return $scalar; # fn(); # here also list of one item
    return ( $scalar, \%hash, \@list ); 
        (fn())[ 0 ]; 
        (fn())[ 1 ]->{ name1 };
        (fn())[ 2 ]->[ 1 ];
    

    it is ok to use $hashTable{$login}, should I be using %hashTable{$login} or does it not matter?

    Nope. You should use $ to access one item from %hashTable. $hashTable{$login} is right.
    If you want to extract two logins you should use @:

    @list =  @hashTable{ 'login1', 'login2' };
    # or
    $l1 = 'login1';
    $l2 = 'login2';
    @list =  @hashTable{ $l1, $l2 };
    

    return \$hashTable{ $login };

    Wrong. You return one item from a hash. So return $hashTable{ $login } is right.

    2.Should my $authHash be really $authHash{ $something }, I am so confused on this

    I suppose your %hashTable is a list of hashes keyed by $login. Like this:

    $login1 = { name => 'Vasiliy',   pass => 'secret' }  # ref to hash
    %login2 = ( name => 'Petrovich', pass => '^&UDHJ' ); # just a hash
    %hashTable = (
        vasya => $login1,  # items are always refs!!!
        piter => \%login2, # items are always refs!!!
    )
    

    So authUser sub will return a reference:

    my $authHash = authUser( 'vasya' ); # & is not required at all
    

    Because of $authHash, if the reference is to a hash you should use ->

    if( $authHash->{ pass } eq $password ) {
    ...
    }
    

    But if your authUser is a parse config file and returns all users, you should to rename it to loadUsers and return a reference to the hash: sub loadUsers { .... return \%hashTable; }

    my $usersDB =  loadUsers;
    
    if( $usersDB->{ $login }->{ pass } eq $password ) {
         print 'You have granged access';
    }
    else { ... }
    

    Edit 2: Can anyone modify my code so that I can understand the answers better?

    Nope. Read my tutorial. To understand how to write code you should do it yourself.

    AS ADVICE

    While you're a newbie:

    • always use hashref and listref.
    • always use -> to access items.
    • always use $ sigil as first char.

    .

    $list = [ 1, 2, 3 ];
    $hash = { a => 1, b => 2 };
    $list->[ 2 ];
    $hash->{ b };
    

    Exceptions will be when you access the whole array or hash:

    @l = @$list;
    %h = %$hash;
    @l = keys %$hash;
    @l = values %$hash;
    

提交回复
热议问题