Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

How could I check if a variable is 'read-only'?

by vladb (Vicar)
on Jan 31, 2002 at 03:22 UTC ( [id://142350]=perlquestion: print w/replies, xml ) Need Help??

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

Hi fair monks!

As I was troubleshooting some old code of mine, I came to a piece of code that was somewhat similar to this test script:
use strict; # clean a directory path of extraneous '/' sub clean_path { for (my $i = 0; $i <= $#_; $i++) { # match two or more '/' between a pair of # other non '/' chars and replace the # multiple occurance of '/' with a single '/'. $_[$i] =~ s|([^/])[/]{2,}([^/])|$1/$2|g; # remove trailing garbage such as '/'... $_[$i] =~ s|[/\n\t\s]+$||; # also remove silly '/./...' things $_[$i] =~ s|\./||g; $_[$i] =~ s|/\.$||g; # some path have that wicked '/foo/bar/.' + '.' in the end! } } ### MAIN my $path_list = undef; ## a bunch of code here ## . . . . . ## ## I'm checking that if no path list was provided, ## I use at a 'blank' path so that the loop would ## execute at least once (as if for root directory). ## foreach my $some_path ( (defined $path_list && $#$path_list >= 0) ? @{ +$path_list} : "" ) { clean_path($some_path); # do stuff with path print "Working with $some_path\n"; } print "done\n";
The error I get when trying to run the script is this:
Modification of a read-only value attempted at test.pl line 10, <IN> + chunk 1. main::clean_path('') called at test.pl line 33
I can't quite understand what causes this error. I'm sure it's something simple. I'm also wondering whether it's possible to test any variable for being a 'read-only' one?

Thanks for help ;-).

"There is no system but GNU, and Linux is one of its kernels." -- Confession of Faith

Replies are listed 'Best First'.
Re: How could I check if a variable is 'read-only'?
by japhy (Canon) on Jan 31, 2002 at 04:13 UTC
    The problem is that you did:
    for $x (this ? (...) : "") { # code that modifies $x }
    When this is false, $x does not get assigned the value "", but rather is aliased to it. And you can't change "" since it's a constant string.

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker.
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

(crazyinsomniac) Re: How could I check if a variable is 'read-only'?
by crazyinsomniac (Prior) on Jan 31, 2002 at 04:22 UTC
    First, results of a search for read-only.

    Read-only variable in foreach loop? References, Prototypes, and read-only values Modification of a read-only

    Next, a first for me

    F:\dev\vladb>perl -MO=Deparse readonly.pl Can't call method "PADLIST" on an undefined value at C:/Perl/lib/B/Dep +arse.pm li ne 1039. CHECK failed--call queue aborted.
    And now an attempt an an explanation.

    perlsyn explains this well, but is going on when $path_list is the empty string (""), what your *ugly* code breaks down to is

    foreach my $some_path ( (defined $path_list && $#$path_list >= 0) ? @{$path_list} : "" ) { clean_path($some_path); # $some_path is now "" ## i'd rewrite it as foreach my $some_path ( (defined $path_list and ref($path_list) eq 'ARRAY' and scalar @{$path_list} ) ? @{$path_list} : '') {
    anyway, as I was saying, you ought to check if $path_list is a reference to an ARRAY, and if it's not, you ought to return one([] what I meant was, undef, or (), or maybe even @{[]}, but definetly not '', hopefullly you get the picture) instead of the empty string(""), because what happens is the same thing I demonstrate with the following one line perl -e"for my $orK(1,2,3,4) { $orK =~ s/.//g; } " which would also throw the read-only error. Why, because (1,2,3,$foo,$4) is not a data structure (an array). It is a list. The for loop implies list context. And since it is not an array, $orK, which becomes the alias for each element of the list, points to hard coded members of a list, which are read only. The following perl -e"@f=qw,1 2 3 4,;for my $orK(@f){ $orK =~ s/.//g; }" gets no read-only error. Why? cause $orK becomes an alias for each member of the list, which in this case is a reference to $f[0] which is a data structure. Its pretty much the same as perl -e"@f=qw,1 2 3 4,;for my $orK($f[0],$f[1],$f[2],$f[3]){ $orK =~ s/.//g; }" That's it. Try it out, read perlsyn again, read japhys excellent context tutorial (List is a 4 letter word, it's on his website, link to which is on his homenode).

    I'm done.

     
    ______crazyinsomniac_____________________________
    Of all the things I've lost, I miss my mind the most.
    perl -e "$q=$_;map({chr unpack qq;H*;,$_}split(q;;,q*H*));print;$q/$q;"

      Thanks tons ;-)).

      Yeah, again, as I mentioned this was an old code, therefore it's *ugly* ;-(. I would try to avoid such code from now onwards.

      Thanks all for your comments. $path_list is actually always an ARRAY reference (although, it's hardly obvious from the code ;-); therefore, I went into the trouble of checking whether it contained any elements and if not I simply made sure that the loop would run at least once (with $some_path set to ""). Frankly, I never knew that $some_path was just an 'alias' to the value. THat's definitely new to me hehe. However, the code doesn't break when there's at least 1 element in the array pointed to by the $path_list scalar. That's where I was confused...

      "There is no system but GNU, and Linux is one of its kernels." -- Confession of Faith
Re: How could I check if a variable is 'read-only'?
by gav^ (Curate) on Jan 31, 2002 at 03:39 UTC
    Ick at the $#_, that is shocking coding when you can write:
    foreach (@_) { # use $_ here }
    And what's wrong with:
    foreach my $some_path (@$path_list) { # etc }
    Have you looked into using File::Spec (standard with Perl I think). I think using canonpath and/or no_upwards might do what you're looking for in an easier to maintain and more portable manner.

    Hope this helps...

    gav^

Re: How could I check if a variable is 'read-only'?
by theorbtwo (Prior) on Jan 31, 2002 at 18:05 UTC
      I've talked about the Scalar::Util module before, and once again it comes to the rescue!
      use Scalar::Util qw(readonly); ... if (readonly $x) { ... }
      Get used to Scalar::Util and List::Util now - they're standard modules in 5.8.
      Cleaner code is:
      sub is_readonly { eval {my $save=$_[0]; $_[0]=$save;}; return $@ =~/read.?only/; }
      Scalar::Util did not work for me.
        Scalar::Util did not work for me

        I would expect Scalar::Util to perform the task flawlessly.
        Could you provide an example script that demonstrates its failing ?

        Cheers,
        Rob
Failing to call a function with a reference?
by metadoktor (Hermit) on Jan 31, 2002 at 03:37 UTC
    I'm not sure if your $some_path variable is a string or an array but the problem likely stems from failing to provide the clean_path function with a reference (so that you could modify the var in the function).

    metadoktor

    "The doktor is in."

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://142350]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (5)
As of 2024-03-28 07:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found