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

I want to create a scalar named by a string. So, given string "foo" I end up with scalar $foo. The idea is to concatenate strings "foo" and "bar" within a subroutine so as to create the name of arbitrary scalar $foobar.

Two examples, reduced to absurdity, follow...

EXAMPLE 1:

sub N_scalars { my ($prefix, $qty) = @_; ...unknown magic here... }
...such that if I later call...
N_scalars("foo",5);
...it initializes five scalars as if I had done...
$foo_1 = ''; $foo_2 = ''; $foo_3 = ''; $foo_4 = ''; $foo_5 = '';
EXAMPLE 2:
sub arb_scalars { foreach $foo (@_) { ...unknown magic here... } }
...so that if I later call...
arb_scalars("alfa","bravo","charlie");
...it works as if I had done...
$alpha = ''; $bravo = ''; $charlie = '';
I know that looks ridiculous, but it is a useful way to automate other subs. I can do it in PostScript simply by converting a string to a variable. The real world use I am shooting for is more complicated. But if the above could be done, then I could also do what it is I have in mind. I could get around it with references, but the result would be unreadible a hundred lines further down the file. To use named scalars would be much preferable. But my sub, to be flexible, ought be fed a list of strings from which to create its vars.

I'm guessing it might be impossible. Does anyone know?

Thanks,

Gan

update (broquaint): dropped <pre> tags and added formatting

Replies are listed 'Best First'.
(jeffa) Re: create arbitrarily named scalars
by jeffa (Bishop) on Jul 26, 2003 at 02:41 UTC
    What Anony is saying is that you don't need "arbitrarily named scalars" - you need arrays.

    Example 1

    N_scalars("foo",5); --yields-- $foo_1 = ''; $foo_2 = ''; $foo_3 = ''; $foo_4 = ''; $foo_5 = '';
    Solution: use an array!
    my @foo;
    Whether you need 5 foos, or 50 foos, or 5,000,000 foos, @foo will hold them (as long as you have the necessary memory available). The only price you have to pay to use an array is remember that you start counting them at zero, not one. But most of the time you won't even have to count them, or even care how many you have:
    open FH, 'foo.txt' or die "$!: can't open foo.txt\n"; my @file = <FH>; close FH;
    Now @file contains as many lines as are in the file foo.txt (as long as that file is the same directory that you ran that code in). Regardless of how many lines there are, these snippets will print them all:
    foreach my $line (@file) { print $line; }
    foreach (@file) { print $_; }
    print foreach @file;
    print @file;
    for my $i (0..$#file) { print $file[$i]; }
    for (0..$#file) { print $file[$_]; }
    print for @file;
    print @file;

    Example 2

    arb_scalars("alfa","bravo","charlie"); --yields-- $alpha = ''; $bravo = ''; $charlie = '';
    Solution: use an array! (an associative array)
    my %word = ( alpha => 'a', bravo => 'b', charlie => 'c', );
    Now i can find out what character belongs to 'charlie' like so:
    print $word{'charlie'};
    You don't need to create an arbitrary variable named $charlie. As a matter of fact, imagine having to pass 26 variables to a subroutine ... wouldn't you rather pass one hash instead? I would.

    So, now that you know why you don't need 'arbitrary variables' in Perl, and you know that you can have them anyway ... why should you not use them? Well, my number one reason for not using them is because i think they are more trouble than they are worth. (passing one var vesus 26 ...)

    Interestingly enough, PHP allows you to use 'arbitrary variables'. extract allows you to turn a hash's keys into varibles and compact will take a list of variables and create a new hash from them. You can do this in Perl1, but if you use strict in your script, then you have to declare the 'arbitrary variables' before you called explode (if such a sub existed in Perl). Having to declare them ahead of time isn't very 'arbitrary', and it is a major nuisance if you don't even need to know what they will be named.

    1 I think that an explode for Perl would be pure abuse, but compact might be useful for creating objects with arbitrary attributes ... surely this is a already CPAN module ... ;)

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: create arbitrarily named scalars
by Zaxo (Archbishop) on Jul 26, 2003 at 01:32 UTC

    I'm not advocating what you want (I always suspect there is a better design that doesn't need this kind of thing), but,

    sub arb_scalars { eval "use vars qw/@{[map {'$'.$_} @_]}/;"; warn $@ and return if $@; 1; } arb_scalars qw/foo bar baz/; { local $, = " "; print %::; }
    The print reveals the presence of *main::foo, *main::bar, and *main::baz in the symbol table. I did that to avoid autovivifying.

    Your numbered sequence of variables is crying out to become an array.

    After Compline,
    Zaxo

Re: create arbitrarily named scalars
by Anonymous Monk on Jul 26, 2003 at 01:09 UTC
Re: create arbitrarily named scalars
by aplonis (Pilgrim) on Jul 26, 2003 at 21:30 UTC
    Okay, I give. You guys convince me. Thanks. 
    
    FWIW my problem was that end user (a comittee, alas) could 
    not make up its mind what all to put into a Perl/Tk form by 
    way of entry widgets. I was sick and tired of re-writing 
    them over every time. So I wanted a way to auto-instantiate 
    them from a list.
    
    You convince me to go with hashes, as below. Now I can
    re-write the rest of the code to test for definedness of
    entry box data by row (if hash exists) and by column (if
    value exists). If I am careful, then when next they change
    their collective mindes, I can just change the hash values
    and the rest should track.
    
    This works for me on both Win2K and NetBSD, and time presses
    so I am going with it. Pray this method offends no-one.
    
    
    #!c:\perl\bin\perl.exe
    #!/usr/pkg/bin/perl
    
    use Tk;
    use strict;
    
    use vars qw/$mw $col %row_1 %row_2 %row_3/;
    
    # The main window.
    $mw = MainWindow->new(-title=>'Fatigue Tracking Sheet');
    
    %row_1 = (
    	parent_frame => $mw,
    	label_width => 12, entry_width => 10,
    	label_1 => 'Alpha 1',
    	label_2 => 'Bravo 2',
    );
    %row_2 = (
    	parent_frame => $mw,
    	label_width => 12, entry_width => 10,
    	label_1 => 'Charlie 3',
    	label_2 => 'Delta 4',
    	label_3 => 'Echo 5',
    	label_4 => 'Foxtrot 6',
    );
    %row_3 = (
    	parent_frame => $mw,
    	label_width => 12, entry_width => 10,
    	label_1 => 'Hotel 7',
    	label_2 => 'India 8',
    	label_3 => 'Juliet 9',
    	label_4 => 'Kilo A',
    );
    
    sub mk_entrybox_row {
    	my ($row_ref,) = @_;
    	$$row_ref{'row_frame'} = $$row_ref{'parent_frame'}->Frame(
    		-relief=>'flat',
    		-borderwidth=>5);
    	for ($col = 1; $col < 9; $col++){
    		last unless (defined($$row_ref{"label_$col"}));
    		$$row_ref{"label_$col"} = $$row_ref{'row_frame'}->Label(
    			-width=>$$row_ref{"label_width"},
    			-text=> $$row_ref{"label_$col"});
    		$$row_ref{"entry_$col"} = $$row_ref{'row_frame'}->Label(
    			-width=>$$row_ref{"entry_width"},
    			-textvariable=>\$$row_ref{"textvar_$col"},-font =>'courier',
    			-background=>"white",-foreground=>'blue');	}
    	for ($col = 1; $col < 9; $col++){
    		last unless (defined($$row_ref{"label_$col"}));
    		$$row_ref{"label_$col"}->pack(-side=>'left');
    		$$row_ref{"entry_$col"}->pack(-side=>'left',-expand=>1,-fill=>'x');
    
    	}
    	$$row_ref{"row_frame"}->pack(-side=>'top',-expand=>0,-fill=>'x');
    }
    
    mk_entrybox_row(\%row_1);
    mk_entrybox_row(\%row_2);
    mk_entrybox_row(\%row_3);
    
    # Backup results and quit program.
    sub quit_MainLoop {
      $mw->destroy() if Tk::Exists($mw);
    }
    
    MainLoop;
    
      Untested, but you could store those hashes (%row_1 %row_2 %row_3) in an array:
      use vars qw/$mw $col @row/; @row = ( { parent_frame => $mw, label_width => 12, entry_width => 10, label_1 => 'Alpha 1', label_2 => 'Bravo 2', }, { parent_frame => $mw, label_width => 12, entry_width => 10, label_1 => 'Charlie 3', label_2 => 'Delta 4', label_3 => 'Echo 5', label_4 => 'Foxtrot 6', }, { parent_frame => $mw, label_width => 12, entry_width => 10, label_1 => 'Hotel 7', label_2 => 'India 8', label_3 => 'Juliet 9', label_4 => 'Kilo A', }, ); # and then later: mk_entrybox_row($_) for @row;
      much easier!!

      Oh, by the way, i personally consider it bad style (or false laziness) to wrap your entire writeup in code tags. Try to only use code tags for code (sometimes output) and use <p> tags to separate your paragraphs. See Perl Monks Approved HTML tags for more info.

      jeffa

      L-LL-L--L-LL-L--L-LL-L--
      -R--R-RR-R--R-RR-R--R-RR
      B--B--B--B--B--B--B--B--
      H---H---H---H---H---H---
      (the triplet paradiddle with high-hat)