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

Hi all,

I'm parsing an XML file using XML:Parser, with the Style set to Subs. What this means is that, when the parser hits <name> it calls the sub 'name'. When it hits </name> it calls the sub 'name_' . I'm using these subs to turn state variables ($state{name}), on and off so that the char_handler knows where to dump the text held between the tags. The problem is that I'm having to write the start and end subroutine for every tag, and they only differ in the name of the state variable.

My question is this : is there any way of generating a subroutine for every element of an array. Thus, if I have an array @tags=(name, gender, address) is there a way of generating the subs name, name_, gender, gender_, description, description_, where each start subroutine turns a state variable named after the tag on, and the end subroutines turn it off ?

Any aid in pursuing the goal of elegance would be greatly appreciated.

Paul
  • Comment on generating subroutines from array variables

Replies are listed 'Best First'.
Re: generating subroutines from array variables
by Kanji (Parson) on Aug 17, 2003 at 16:55 UTC

    Another option is to use the AUTOLOAD feature, and determine valid tag toggles using a hash rather than an array (although there's no reason you couldn't generate the former from the latter)...

    my %state = ( name => 0, # default states gender => 0, address => 0, ); sub AUTOLOAD { my $tag = $AUTOLOAD; $tag =~ s/^.*:://; my $turn_off = $tag =~ s/_$//; # Only keep track of tags we want. return unless exists $state{$tag}; $state{$tag} = $turn_off ? 0 : 1; # Do something that uses %state whatever(); }

        --k.


Re: generating subroutines from array variables
by mattriff (Chaplain) on Aug 17, 2003 at 15:56 UTC
    You could do something like:

    my @list = qw/foo bar baz/; foreach my $element (@list) { *{$element} = sub { $state{$element} = 1; }; *{"${element}_"} = sub { $state{$element} = 0; }; }

    If you are using strict {insert standard admonition here} you'd need to use:

    no strict 'refs';
    in the loop, I believe.

    On another note, without knowing just what your goal is, there is likely another XML module that might do more of what you need for you. A search at search.cpan.org for "XML" returns over 2000 matches. :)

    UPDATE: Added the missing semi-colons. Thanks to esh for pointing out they were missing. :)

    - Matt Riffle

      Slight syntax correction to mattriff's cool code. You'll need to terminate the assignments with semicolons. (Well, this being Perl, you just need to separate them with semicolons, but let's pretend it's terminate so we don't run into problems the next time we add a statement).

      my @list = qw/foo bar baz/; foreach my $element (@list) { *{$element} = sub { $state{$element} = 1; }; *{"${element}_"} = sub { $state{$element} = 0; }; }

      -- Eric Hammond

Re: generating subroutines from array variables
by gwadej (Chaplain) on Aug 18, 2003 at 02:21 UTC

    Although I really like the AUTOLOAD approach, I've used a different method in the past.

    WARNING: The following code could cause nasty personal side effects, not limited to people running when they see you.

    my @tags= qw/name gender address/; foreach my $t (@tags) { eval "sub $t { \$state{'$name'} = 1; }"; eval "sub ${t}_ { \$state{'$name'} = 1; }"; }

    Of course, if you find that one too disgusting. You could always run a loop once to write the appropriate code to a Perl module file and then use that file.

    BEGIN { unless(-e "Tags.pm") { my @tags= qw/name gender address/; open( F, ">Tags.pm" ) or die "Unable to create Tags.pm:$!"; foreach my $t (@tags) { print F "sub $t { \$state{'$name'} = 1; }\n"; print F "sub ${t}_ { \$state{'$name'} = 1; }\n"; } print F "1;\n"; close( F ) or die "Unable to close Tags.pm:$!"; } } use Tags;

    Ain't code that writes code a wonderful thing.

    Update: I forgot to add the "1;" at the end.

    G. Wade

Re: generating subroutines from array variables
by bunnyman (Hermit) on Aug 18, 2003 at 20:53 UTC
      many thanks for everybody's help. Worked a dream.