gorillaman has asked for the wisdom of the Perl Monks concerning the following question:

I am pulling my hair out trying to figure this out.

I am trying to pass a hash and a separate scalar to a subroutine. The hash contains the new data that will go into the database, and the scalar indicates the account to make the changes to. But at this point, the sub mixes everything together into a pile of goo. The scalar gets mixed into the hash, and the hash turns into an array.

I've tried using the docs, but I only got more confused, I think I might be missing something elementary here.

%new_stuff = ("RATING" => 4.99); &db_modify_account("jdonald", %new_stuff); sub db_modify_account { my %new_data = @_; while (($key, $value) = each %new_data) { print "The $key is: $value.<BR>"; } }

Replies are listed 'Best First'.
Re: Passing a hash, and a scalar to subroutine
by davido (Cardinal) on Jul 17, 2004 at 07:36 UTC
    There are two solutions to your problem.

    The first will work, has already been suggested, but isn't going to give you much mileage past this particular case. That is, to shift the scalar out of @_ before assigning the remaining contents of @_ to your hash.

    But the proper way to pass more than one complex variable (such as an array or a hash) into a subroutine is to pass a reference to the actual entity, rather than the entity itself. That is done like this:

    my %new_stuff = ( "RATING" => 4.99 ); db_modify_account( "jdonald", \%new_stuff ); sub db_modify_account { my( $uid, $ref_new_data ) = @_; while ( ( $key, $value ) = each %{ $ref_new_data } ) { print "The $key is: $value.<br />"; } print "Oh, and the user is $uid.<br />"; }

    The thing to keep in mind is that all arguments passed to a sub get flattened out into a single list. Also remember that a hash, in list context, becomes a flat list of key, value, key, value, etc.

    So your current implementation passes the following list to the sub: "jdonald", "RATING", 4.99. And when the hash is reconstructed, under your current implementation, it is mapped out like this: jdonald => RATING, 4.99 => undef

    Passing the references allows you to avoid the trickery it would take to figure out how to rebuild the complex data into a hash, array, or whatever. Imagine trying to pass two hashes, or two arrays as flat lists, and figuring out where one ends and the next begins. Passing references is a whole lot easier.

    This is discussed in a bit more detail in perlsub, perlref, and perlreftut.


    Dave

Re: Passing a hash, and a scalar to subroutine
by meredith (Friar) on Jul 17, 2004 at 05:02 UTC

    Insert this line at the start of the sub, before %new_data is initialized:

    my $scalar_data = shift;

    See perldoc -f shift for an explanation of shift, and what it does with no parameters. I should note that you had already selected the proper order (scalar then hash) to do this as easily as possible.

    mhoward - at - hattmoward.org
      Ahh.. Thank you so much, that definetely cleared things up. The "shift" was exactly what I needed. This one had me stumped. I have a lot to learn. It seems the more I learn, the dumber I get. Anyway, Thank you!
        A hash in list context, such as a subroutine argument list, gets converted to a list of key,value pairs. So you are actually passing three arguments in your example code. One is the scalar, and the key and value of the single hash entry. Inside the sub, you convert the argument list back into a hash. The rule here is that the elements in the list are entered into the hash as key1 => value1, key2 => value2 and so forth. (The "=>" is just another way of writing "," but it emphasizes the pairing between key and value.) So your three elements get assigned into the hash as scalar => key1, value1 => undef, which makes a "hash" out of the whole deal. Since you only have three elements, Perl helpfully supplies the undef value as the fourth value in the list. Doing the shift removes the scalar from the argument list and leaves two elements, which assign back into the hash as you desire.
Re: Passing a hash, and a scalar to subroutine
by murugu (Curate) on Jul 17, 2004 at 06:49 UTC

    I think u have to shift the scalar first, and then shift the entire remaining content into the hash;

    sub db_modify_account { my $a = shift; my %new_data=@_; while (($key, $value) = each %new_data) { print "The $key is: $value.<BR>"; } }

    or else U can use passing by references.

    see perldoc for perlreftut.