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

Fellow monks,

I've always sort of wondered if there was a way to simulate this in Perl due to sheer laziness and hating to type more than I have to.

In Pascal you can say:

with bbentry do begin name := "Olivia Paarts"; stdcode := "3214"; phnum := "30001234"; yrlevel := 9; end

and what I'd like to be able to do in Perl is something along the lines of:

with $anonhash do { $name = 'dave'; $city = 'San Francisco'; $phone = '555-1212'; $hobbies = qw( Perl Anime); }

I've been trying to figure out a way to do this but I'm coming up empty.

Has anyone ever implemented anything like this before?

Useless trivia: In the 2004 Las Vegas phone book there are approximately 28 pages of ads for massage, but almost 200 for lawyers.

Replies are listed 'Best First'.
Re: Duplicating Pascal's with statement in Perl for anonymous data structures
by Roy Johnson (Monsignor) on Mar 28, 2005 at 18:22 UTC
    Is this close enough?
    for ($anonhash) { $_->{$name} = 'dave'; $_->{$city} = 'San Francisco'; # etc }

    Caution: Contents may have been coded under pressure.
Re: Duplicating Pascal's with statement in Perl for anonymous data structures
by ikegami (Patriarch) on Mar 28, 2005 at 18:25 UTC

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

    Update: Fixed the error pointed out by Tanktalus.

      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.

      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.

Re: Duplicating Pascal's with statement in Perl for anonymous data structures
by ambrus (Abbot) on Mar 28, 2005 at 18:19 UTC

    One option is to do this:

    %$anonhash = (%$anonhash, name => "dave", city => "San Francisco", ... );
    Of course, this way you can not read the values from the hash, only write them, and the changes are written to the hash at once, not with each key.

    Another similar option is

    @$anonhash{qw"name city ..."} = ("dave", "San Francisco", ...);
Re: Duplicating Pascal's with statement in Perl for anonymous data structures
by Roy Johnson (Monsignor) on Mar 28, 2005 at 21:52 UTC
    I got it to do most of what I wanted it to: you reference the members as scalars; you can execute whatever code you want; if you refer to a variable that isn't a member name, it uses the ordinary variable (in fact, if you have a lexical with the same name, it will ignore the member).

    But you have to turn off strict, and it definitely pollutes the main namespace (or another namespace, if you litter your code with package statements). So I don't really recommend it, but it can be done..

    use warnings; my %hash = (foo => 'bar', a => 'x'); sub with (\%&) { my ($href, $cref) = @_; *$_ = \$href->{$_} for keys %$href; $cref->(); } print "$_: $hash{$_}\n" for keys %hash; my $z = 6; with %hash, sub { print "** $foo **!\n"; $a = 5; $z = 'changed lexical'; } ; print "$_: $hash{$_}\n" for keys %hash; print "Z is $z\n";
    Update: got rid of string eval, at least.

    Caution: Contents may have been coded under pressure.