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

I seek the wisdom of the monastery.

I can define a named hash using map:
#!/usr/bin/perl $char = 0xE000; %characters = map { $_ => $char++ } ( 'first character', 'second character', 'third character' ); printf "First: %X; third: %X\n", $characters{'first character'}, $characters{'third character'};
How do I define an anonymous hash using map?

I ask because defining character aliases under use charnames requires a similar syntax but using an anonymous hash. I can list the entries one by one:

#!/usr/bin/perl use charnames ":alias" => { 'first character' => 0xE000, 'second character' => 0xE001, 'third character' => 0xE002 }; printf "First: %X; third: %X\n", ord("\N{first character}"), ord("\N{third character}");
But this clearly doesn't scale as well and is more error-prone if there are many entries. However, I can't find the right syntax for making that work using map.

Replies are listed 'Best First'.
Re: create an anonymous hash using "map"
by jwkrahn (Abbot) on Aug 07, 2024 at 00:54 UTC
    %characters = map { $_ => $char++ } ( 'first character', 'second character', 'third character' );

    How do I define an anonymous hash using map?

    $characters = { map { $_ => $char++ } 'first character', 'second character', 'third character' };
    Naked blocks are fun! -- Randal L. Schwartz, Perl hacker
      Thanks for the reply, jwkrahn! I'm still not sure how to work this into a charnames alias list, though. This:
      #!/usr/bin/perl use charnames ":alias" => { map { $_ => $char++ } 'first character', 'second character', 'third character' }; printf "First: %X; third: %X\n", ord("\N{first character}"), ord("\N{third character}");
      produces the error:
      Unknown charname 'first character' at ./mytest line 10, within string Execution of ./mytest aborted due to compilation errors.
      So it accepts the syntax of the alias setup, but doesn't actually create the aliases.

      If you're suggesting I use your assignment statement as written and then use your $characters variable to do the alias setup, I can't even find the right syntax for that. This gives syntax errors on the use charnames line, as I would expect (as did a couple variants I tried):

      #!/usr/bin/perl $characters = { map { $_ => $char++ } 'first character', 'second character', 'third character' }; use charnames ":alias" => $characters;
        Actually, there is a second issue besides constructing a literal anonymous hash, which is complicating your case.

        It's the difference between run time vs compile time behavior.

        use will execute at compile time, so does your construct. Any code you depend on to be run before must also be executed at compile time, like in a BEGIN block.

        See perlmod for more on execution phases.

        Update

        And here a suggestion which seems best to be maintained IMHO.

        (untested hack into mobile browser)

        my %characters; BEGIN { my $char = 0xE000; %characters = map { $_ => $char++ } ( 'first character', 'second character', 'third character' ); } use charnames ":alias" => \%characters;

        you may also consider putting your rather complicated specialized configuration array into a module of your own, which is executed with use

        This will automatically happen at compile time, and provide you with a clean interface

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

Re: create an anonymous hash using "map"
by parv (Parson) on Aug 07, 2024 at 08:22 UTC

    Convert use charnames into BEGIN { ...; require charnames; ... } to be able to use %hash as the value for :alias export tag ...

    # cat custom-name.pl; perl custom-name.pl use strict; use warnings; use Data::Dump qw[dump]; BEGIN { my $char = ord( q[a] ); my %hash = map { $_ => $char++ } qw[ aey bee ]; print dump( %hash ), qq[\n]; use v5.16; require charnames or die $@; charnames->import( q[:alias] => { %hash, q[cea] => $char } ); } printf qq[aey: %s\ncea: %s\n], qq[\N{aey}], qq[\N{cea}] ; ("bee", 98, "aey", 97) aey: a cea: c

    ... thanks to the response at https://stackoverflow.com/a/63018246, found via search for "perl begin use require export" as the documentation for use & require was inadequate.

    Later I checked again & found use -- what I was really looking for (and realized MetaCPAN makes for a bad search engine for perl non-module documentation)!

Re: create an anonymous hash using "map"
by 1nickt (Canon) on Aug 07, 2024 at 10:58 UTC

    Here's one way:

    #!/usr/bin/perl use strict; use warnings; my $char; BEGIN { $char = 1; } use charnames ":alias" => { map { $_ => $char++ } ( 'first character', 'second character', 'third character' )}; printf "First: %X; third: %X\n", ord("\N{first character}"), ord("\N{third character}");

    Note: Using 0 as a value for $char didn't work.

    Hope this helps!

    update: fixed typo

    The way forward always starts with a minimal test.
Re: create an anonymous hash using "map"
by raygun (Scribe) on Aug 09, 2024 at 05:21 UTC
    Thank you, everyone! I have somehow never needed a BEGIN block (or any of perl's named blocks) before, so it never would have occurred to me to even search there. But LanX's explanation for why it's needed makes sense to me.
        The practical uses of BEGIN and END in Perl may be more apparent when you consider how these were first used in awk. Perl of course took that idea and ran with it. As a Perl programmer, understanding this part of awk helped, especially with one-liners.

        Nit: sub name {...} is almost equivalent to BEGIN { *name = sub {...}; }. The latter has no name associated with it, which matters in traces.

        $ perl -e' use Carp qw( croak ); sub foo { croak "bar" } foo(); ' bar at -e line 3. main::foo() called at -e line 4
        $ perl -e' use Carp qw( croak ); BEGIN { *foo = sub { croak "bar" }; } foo(); ' bar at -e line 3. main::__ANON__() called at -e line 4

        There are ways of naming the anon sub to get a BEGIN-using equivalent.

        $ perl -e' use Carp qw( croak ); use Sub::Util qw( set_subname ); BEGIN { *foo = set_subname "foo", sub { croak "bar" }; } foo(); ' bar at -e line 4. main::foo() called at -e line 5