LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Perl - Can't use string ("html") as an ARRAY ref while "strict refs" (https://www.linuxquestions.org/questions/programming-9/perl-cant-use-string-html-as-an-array-ref-while-strict-refs-746065/)

OldGaf 08-08-2009 12:19 PM

Perl - Can't use string ("html") as an ARRAY ref while "strict refs"
 
The below snippet works fine until I use strict. Then it dies with the following error:

Quote:

Can't use string ("html") as an ARRAY ref while "strict refs" in use at ./filetest3 line 18.
I want to create @lists based on the $scalars in @type. However, "my @$ext = ()"; and push (@$ext, @files); do not play nice with strict. How do I get around this?

Quote:

#!/usr/bin/perl -w

system clear;

use strict;
use warnings;
use File::List;

my @type = ('html', 'mp3', 'txt');
my @files;
my $ext;
my $search;
my $file;
my $path;

foreach $ext (@type) {

"my @$ext = ()";

}

print "Enter path:";
chomp($path = <STDIN>);

foreach $ext (@type) {
$search = new File::List("$path");
@files = @{ $search->find("\.$ext\$")};

push (@$ext, @files);

}

Telemachos 08-08-2009 04:54 PM

If I understand what I'm looking at (and I'm not sure I do), you want to create an array of files for each extension. Since the module produces an array reference, you can easily put each of those into an array and have an array of arrays. Each array reference in the main array will have a list with all the files found with that extension. This should do what you want:

Code:

#!/usr/bin/perl
use strict;
use warnings;
use File::List;

my @extensions = ('html', 'mp3', 'txt');
my @files_by_extension;

print "Enter path: ";
chomp(my $path = <STDIN>);
my $search = new File::List($path);

foreach my $ext (@extensions) {
  push @files_by_extension, $search->find("\.$ext\$");
}

use Data::Dumper;
print Dumper @files_by_extension;

Sample output:

Code:

Enter path: /home/telemachus/test
$VAR1 = [
          '/home/telemachus/test/further/down/buzz.html',
          '/home/telemachus/test/further/bar.html',
          '/home/telemachus/test/foo.html'
        ];
$VAR2 = [
          '/home/telemachus/test/further/down/buzz.mp3',
          '/home/telemachus/test/further/bar.mp3',
          '/home/telemachus/test/foo.mp3'
        ];
$VAR3 = [
          '/home/telemachus/test/further/bar.txt',
          '/home/telemachus/test/further/down/buzz.txt',
          '/home/telemachus/test/foo.txt'
        ];


OldGaf 08-08-2009 05:36 PM

Thanks Telemachos.

That is the jist of what I wanted.... tho I was trying to make the lists / arrays separate.

Can anyone tell me why what I was doing fails only when using strict?

Telemachos 08-08-2009 05:59 PM

Quote:

Originally Posted by OldGaf (Post 3636124)
Can anyone tell me why what I was doing fails only when using strict?

Because you're trying to use a variable name, $ext, to create a variable name, @$ext. This is a use of symbolic references, and it's one of the main things that strict disallows.

See these two links:
http://perldoc.perl.org/strict.html
http://perldoc.perl.org/perlref.html...lic-references

You can actually do this in Perl (use a variable as a variable name), but it's a terrible idea. See here: http://perl.plover.com/varvarname.html

Telemachos 08-08-2009 09:07 PM

Quote:

Originally Posted by OldGaf (Post 3636124)
That is the jist of what I wanted.... tho I was trying to make the lists / arrays separate.

I've never used the File::List module before, and I'm not entirely nuts about its API. Here's how I might produce separate arrays using File::Find:

Code:

#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;

my $html = 'html';
my $mp3 = 'mp3';
my $txt = 'txt';
my (@htmls, @mp3s, @txts);

print "Give me the path: ";
chomp(my $path = <STDIN>);

find(\&insert_em, $path);

sub insert_em {
  return if -d $_;
  if ($_ =~ /$html$/) {
    push @htmls, $File::Find::name;
    return;
  }
  if ($_ =~ /$mp3$/) {
    push @mp3s, $File::Find::name;
    return;
  }
  if ($_ =~ /$txt$/) {
    push @txts, $File::Find::name;
    return;
  }
}

use Data::Dumper;
print Dumper \@htmls, \@mp3s, \@txts;

In some ways, it feels wrong to hard-code the names that way, but if you know that those are the three types of files you're looking for, I don't see too much harm in it. In any case, it saves you from symbolic references.

OldGaf 08-09-2009 08:28 AM

Quote:

Originally Posted by Telemachos (Post 3636218)
I've never used the File::List module before, and I'm not entirely nuts about its API. Here's how I might produce separate arrays using File::Find:

Code:

#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;

my $html = 'html';
my $mp3 = 'mp3';
my $txt = 'txt';
my (@htmls, @mp3s, @txts);

print "Give me the path: ";
chomp(my $path = <STDIN>);

find(\&insert_em, $path);

sub insert_em {
  return if -d $_;
  if ($_ =~ /$html$/) {
    push @htmls, $File::Find::name;
    return;
  }
  if ($_ =~ /$mp3$/) {
    push @mp3s, $File::Find::name;
    return;
  }
  if ($_ =~ /$txt$/) {
    push @txts, $File::Find::name;
    return;
  }
}

use Data::Dumper;
print Dumper \@htmls, \@mp3s, \@txts;

In some ways, it feels wrong to hard-code the names that way, but if you know that those are the three types of files you're looking for, I don't see too much harm in it. In any case, it saves you from symbolic references.

Actually, this is not the complete script...... just a bit. I will be using STDIN to populate an array to hold what ever extensions need to be found. That is why I want to create several arrays based on the content of the extensions array.

Quote:

Because you're trying to use a variable name, $ext, to create a variable name, @$ext. This is a use of symbolic references, and it's one of the main things that strict disallows.
I have not read the link yet (though I will for sure) but I must say that I can think of many times when creating variables with symbolic references would be useful. Not being able to seems to remove alot of flexibility.

Quote:

You can actually do this in Perl (use a variable as a variable name), but it's a terrible idea.
Are you saying I can do it and still use strict? If so..... please tell me how.

OldGaf 08-09-2009 09:06 AM

My best solution if I must use variables with symbolic references may be this I guess... ?

Quote:

As you can see, the strict module imposes a very rigid environment for developing applications. But, that's a very nice and powerful feature, because it helps us track down a variety of bugs. In addition, the module allows for great flexibility as well. For example, if we know that a certain piece of code works fine, but will fail under strict, we can turn certain restrictions off, like so:

## code that passes strict
...
{
no strict; ## or no strict "vars";

## code that will not pass strict
}

All code within the block, delimited by braces, will have no restrictions.

Though I will not be entirely happy with it. :O(

Telemachos 08-09-2009 10:27 AM

Quote:

Originally Posted by OldGaf
Are you saying I can do it and still use strict? If so..... please tell me how.

No, sorry if I was unclear. I meant that you can do it in Perl if you don't use strict or you temporarily disable it (as you figured out yourself). You can't use symbolic references and strict at the same time, as far as I know.

Quote:

Originally Posted by OldGaf
I have not read the link yet (though I will for sure) but I must say that I can think of many times when creating variables with symbolic references would be useful. Not being able to seems to remove alot of flexibility.

This seems to be everyone's first reaction. All I can say is that I never use them, and I find I can do whatever I want in other ways.

Let me take your example for one more spin. You say that you will use STDIN to gather a list of extensions and then you want to populate arrays containing files ending in those extensions. My first thought is a hash of arrays. What I would do is take the items given on STDIN and make those hash keys. The value of each of those would be an anonymous array reference. That way, I could refer to precisely which extension I wanted, whenever I wanted, because the hash key allows specific lookup.

Here's a short implementation, reusing my File::Find code from above.

Code:

#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;

my %files_by_ext;

print "Give me the path: ";
chomp(my $path = <STDIN>);

print "Give me the extensions to search for, separated by spaces: ";
chomp(my @extensions = split /\s+/, <STDIN>);

@files_by_ext{ @extensions } = [];

find(\&insert_em, $path);

use Data::Dumper;
print Dumper \%files_by_ext;

sub insert_em {
  return if -d $_;
  for my $ext (keys %files_by_ext) {
    if ($_ =~ /$ext$/) {
      push @{ $files_by_ext{$ext} }, $File::Find::name;
      return;
    }
  }
}

Sample output:
Code:

telemachus ~ $ perl file_list
Give me the path: /home/telemachus/test
Give me a list of extensions to search for, separated by spaces: mp3 html txt
$VAR1 = {
          'mp3' => [
                    '/home/telemachus/test/foo.mp3',
                    '/home/telemachus/test/further/bar.mp3',
                    '/home/telemachus/test/further/down/buzz.mp3'
                  ],
          'html' => [
                      '/home/telemachus/test/foo.html',
                      '/home/telemachus/test/further/bar.html',
                      '/home/telemachus/test/further/down/buzz.html'
                    ],
          'txt' => [
                    '/home/telemachus/test/foo.txt',
                    '/home/telemachus/test/further/bar.txt',
                    '/home/telemachus/test/further/down/buzz.txt'
                  ]
        };


Sergei Steshenko 08-09-2009 10:52 AM

To OP - believe me (after 15+ years of experience with Perl) - you practically never need variables with dynamically created variable names, since you can always write

Code:

my %vars_with_dynamic_names;
...
$vars_with_dynamic_names{$some_funky_name} = foo;
...
$bar = $vars_with_dynamic_names{$some_funky_name};

;

even more practically never you need to work without

Code:

use strict;
- not using 'strict' is rarely needed in cases of obscure global namespace manipulation which happens in very core Perl modules - you (and even me, despite the 15+ years of experience with Perl) are quite far from it.

In simple English - if your code doesn't work with

Code:

use strict;
your code is guilty until proven innocent, and the latter almost never happens.

theNbomr 08-11-2009 11:14 AM

Quote:

Originally Posted by Telemachos (Post 3636691)
Let me take your example for one more spin. You say that you will use STDIN to gather a list of extensions and then you want to populate arrays containing files ending in those extensions. My first thought is a hash of arrays.

Absolutely correct, IMHO. Surprises me how many posters avoided coming to this conclusion.
--- rod.


All times are GMT -5. The time now is 05:44 PM.