in reply to Duplicating Pascal's with statement in Perl for anonymous data structures

How about:

sub with(\%$) { my ($hash, $values) = @_; $hash->{$_} = $values->{$_} foreach keys %$values; } $anonhash = { id => 'u1234', }; with %$anonhash, { name => 'dave', city => 'San Francisco', phone => '555-1212', hobbies => [ qw( Perl Anime ) ], }; require Data::Dumper; print(Data::Dumper::Dumper($anonhash)); __END__ $VAR1 = { 'id' => 'u1234', 'name' => 'dave' 'city' => 'San Francisco', 'phone' => '555-1212', 'hobbies' => [ 'Perl', 'Anime' ], };

With a slight change, you could keep the word "do" (as seen behind the cut), but it's less efficient (because the new values are passed as a list instead of as a hash ref).

sub with(\%%) { my ($hash, %values) = @_; $hash->{$_} = $values{$_} foreach keys %values; } $anonhash = { id => 'u1234', }; with %$anonhash, do { name => 'dave', city => 'San Francisco', phone => '555-1212', hobbies => [ qw( Perl Anime ) ], }; require Data::Dumper; print(Data::Dumper::Dumper($anonhash)); __END__ $VAR1 = { 'id' => 'u1234', 'name' => 'dave' 'city' => 'San Francisco', 'phone' => '555-1212', 'hobbies' => [ 'Perl', 'Anime' ], };

Update: Fixed the error pointed out by Tanktalus.

Replies are listed 'Best First'.
Re^2: Duplicating Pascal's with statement in Perl for anonymous data structures
by Tanktalus (Canon) on Mar 28, 2005 at 18:49 UTC

    Try it with warnings next time ;-)

    with %$anonhash, { name => 'dave', city => 'San Francisco', phone => '555-1212', hobbies => [ qw( Perl Anime ) ], # make the value into a scalar. };

    I'd also make the minor change:

    sub with(\%$) { my ($hash, $values) = @_; @$hash{keys %$values} = values %$values; }
    Just a bit more idiomatic, IMO.

Re^2: Duplicating Pascal's with statement in Perl for anonymous data structures
by Roy Johnson (Monsignor) on Mar 28, 2005 at 19:12 UTC
    At first glance, I thought you had accomplished what I couldn't. Then I realized you're just passing a hashref, not a code block. The actual with statement in Pascal takes a code block and you can read and write variables by member name, rather than by fully-qualified name.

    I've been trying to mimic that in Perl with globs and package variables, but I think it's not possible. What do you think?


    Caution: Contents may have been coded under pressure.

      I think it's either not possible or is a Bad Thing. Pascal can know what identifiers in the with body refer to record members and which ones are variables. However, a perl hash is an open structure, it can have any key, so perl would have no way to deduce which $-variables are meant to be hash elements.

        Good point to note that you can also use non-member variables within a with. But in the way I envision it working, Perl would do The Right Thing. If only it would work. Here's a quick description:
        with takes a hashref and a coderef. It goes through the hashref and creates package variables in the "With::" namespace that point to the hash elements. Then it executes the coderef in the With package space.

        Sadly, I don't think it can be done. Even in a coderef executed within a package, Perl considers the unqualified variable names to be in main (or wherever the coderef was created). And while my glob-fu is shaky, I don't think it's possible to make a symbol table refer to another hash, so that changes to package variables actually affect members of the hash.

        Here's an eval solution that turns the members into references with the same names, so the user accesses them as, e.g., $$a:

        use strict; my %hash = ( a => 1, b => 2, c => 3 ); sub with(\%&) { my ($href, $cref) = @_; my $cmd = join "\n", map sprintf('local $::%s = \\$href->{%s};', $_, $_), keys %$href; eval $cmd . '$cref->()'; warn "$@: $cmd\n" if $@; } with %hash, sub { $$a = 'changed'; print "B=$$b\n"; } ; use Data::Dumper; print Dumper(\%hash);
        I always feel dirty after using a string eval, though.

        Caution: Contents may have been coded under pressure.