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

Hello Monks,

May be I am not phrasing this question properly. In a nutshell, I expect one argument to be pased to the script. However, I am doing the checking for it in a sub, which eventually receives this argument.

So, I blatently pass $ARGV[0] to the sub regardless of whether any arguement was passed to the script or not. I do the checking of # of arguments in the sub (May be not the right place to do so). I actually exected @_ to contain no entries. Yet, it contains 1. But the retrieved value using shift is undefined.

#! /usr/bin/perl use strict; use warnings; sub p1 { my $n1; $n1=@_; print "1. Parameters to the sub p1 $n1\n"; my $p1=shift; print "2. p1=$p1\n"; } p1 $ARGV[0];
So, here is my academic curiosity question:

Arguments is Perl are passed by reference. Argument $ARGV[0] never existed in memory (If I did not pass anything argument to the script), so there is no address for it. So shouldn't @_ say 0? How else would you (recommend) check for passed arguments to be present if this does not work?

Thanks.

Ash

Replies are listed 'Best First'.
Re: Question: Is undef a valid argument?
by jpearl (Scribe) on May 06, 2009 at 19:35 UTC
    Well, if you are just looking to check if there is anything in it or not (i.e. not checking to see the actual *number* of elements) according to defined :

    "You should instead use a simple test for size:
    if (@an_array) { print "has array elements\n" }
    "
    I suppose you could also do something like scalar(@an_array) to find out the number of elements it currently possesses.
      Thanks JPerl!

      This is elegent and requires same lines of code as I used before.

      Ash

      Sorry,

      Spoke too soon.

      None of what you sggested works.

      #! /usr/bin/perl use strict; use warnings; sub p1 { my $n1; $n1=scalar(@_); if (@_) { print "Has elements\n"; } print "1. Parameters to the sub p1 $n1\n"; my $p1=shift; if (@_) { print "Has elements\n"; } else { print "Has no elements\n"; } print "2. p1=$p1\n"; } p1 $ARGV[0];
      Try passing nothing to the script you will see it still states one argument.

        Like I already said, $ARGV[0] always evaluates to a scalar. As long as you keep using $ARGV[0], you'll always get a scalar. If you want to pass zero when @ARGV is empty, you'll need to pass something different when @ARGV is empty. In general, you'd do

        p1( @ARGV ? $ARGV[0] : () );

        In this case, the following suffices:

        p1(@ARGV);
        You are still explicitly passing the first, non existent element of your array to your subroutine. If instead call your subroutine with

        p1 @ARGV;

        you will get your expected result.

Re: Question: Is undef a valid argument?
by ikegami (Patriarch) on May 06, 2009 at 20:48 UTC

    $scalar, $array[$index] and $hash{$key} always evaluate to a scalar. Should the variable or element not exist, that scalar is undef.

    $ perl -le'@x = $scalar; print 0+@x; print $x[0] // "[undef]"' 1 [undef] $ perl -le'@x = $array[0]; print 0+@x; print $x[0] // "[undef]"' 1 [undef] $ perl -le'@x = $hash{foo}; print 0+@x; print $x[0] // "[undef]"' 1 [undef]
Re: Question: Is undef a valid argument?
by almut (Canon) on May 06, 2009 at 21:52 UTC
    Argument $ARGV[0] never existed in memory (If I did not pass anything argument to the script), so there is no address for it.

    It might be worth noting that undef values actually are scalars (SV) which live at a certain address. Consider this (note the SV = NULL(0x0) at ...)

    use strict; use warnings; use Devel::Peek; sub p1 { my $n1 = @_; print "1. Parameters to the sub p1 $n1\n"; print \$_,"\n" for @_; Dump $_ for @_; } p1 $ARGV[0], undef; __END__ 1. Parameters to the sub p1 2 SCALAR(0x604f80) SCALAR(0x604160) SV = PVLV(0x67eba0) at 0x604290 REFCNT = 3 FLAGS = (GMG,SMG) IV = 0 NV = 0 PV = 0 MAGIC = 0x62f2a0 MG_VIRTUAL = &PL_vtbl_defelem MG_TYPE = PERL_MAGIC_defelem(y) TYPE = y TARGOFF = 0 TARGLEN = 0 TARG = 0x604f80 SV = NULL(0x0) at 0x604f80 REFCNT = 2 FLAGS = () SV = NULL(0x0) at 0x604160 REFCNT = 2 FLAGS = ()
Re: Question: Is undef a valid argument?
by kennethk (Abbot) on May 06, 2009 at 19:28 UTC
    Consider that the code

    #! /usr/bin/perl use strict; use warnings; print scalar @ARGV, "\n"

    outputs 0. By explicitly accessing the non-existent array element, perl extends the array and populates those new values with undefs, and that is what gets passed to your subroutine. This is a manifestation of autovivification.. As ikegami points out, this isn't autovivification, but it is returning an undef as you attempt to access the undefined array element.

      By explicitly accessing the non-existent array element, perl extends the array and populates those new values with undefs,

      No, simply accessing non-existent array elements doesn't extend the array.

      $ perl -le'my @a; print 0+@a; my @x = $a[0]; print 0+@a;' 0 0

      The array is only extended if the element is used as an l-value.

      $ perl -le'my @a; print 0+@a; \$a[0]; print 0+@a;' 0 1 $ perl -le'my @a; print 0+@a; 1 for $a[0]; print 0+@a;' 0 1

      But not every time it's used as an l-value.

      $ perl -le'my @a; print 0+@a; sub {}->( $a[0] ); print 0+@a;' 0 0

      This is a manifestation of autovivification.

      Yes and no.

      The extension of an array or hash can be considered autovivification, but the term usually refers to the creation of new variables by dereferences. Specifically, the document to which you linked only discusses the creation of new variables by deferences, so it's not a useful link.

Re: Question: Is undef a valid argument?
by ig (Vicar) on May 06, 2009 at 23:47 UTC
    So shouldn't @_ say 0?

    Arguments are passed by reference but in your case you end up with an array of one element, that one element being a reference to the non-existent element of @ARGV. So, @_ has one element even though that element is a reference to a non-existent element of @ARGV.

    Consider the following example:

    use strict; use warnings; use Devel::Peek; Dump(\@ARGV); mysub($ARGV[0], $ARGV[1]); mysub(@ARGV); exit(0); sub mysub { print "mysub\n"; Dump(\@_); } __END__ SV = RV(0x83591fc) at 0x83591f0 REFCNT = 1 FLAGS = (TEMP,ROK) RV = 0x83693c0 SV = PVAV(0x835a228) at 0x83693c0 REFCNT = 2 FLAGS = () ARRAY = 0x0 FILL = -1 MAX = -1 ARYLEN = 0x0 FLAGS = (REAL) mysub SV = RV(0x83590dc) at 0x83590d0 REFCNT = 1 FLAGS = (TEMP,ROK) RV = 0x8376420 SV = PVAV(0x835a28c) at 0x8376420 REFCNT = 3 FLAGS = () ARRAY = 0x838a418 FILL = 1 MAX = 3 ARYLEN = 0x0 FLAGS = (REAL) Elt No. 0 SV = PVLV(0x83871d4) at 0x83591f0 REFCNT = 2 FLAGS = (GMG,SMG) IV = 0 NV = 0 PV = 0 MAGIC = 0x839e158 MG_VIRTUAL = &PL_vtbl_defelem MG_TYPE = PERL_MAGIC_defelem(y) TYPE = y TARGOFF = 0 TARGLEN = 1 TARG = 0x83693c0 SV = PVAV(0x835a228) at 0x83693c0 REFCNT = 3 FLAGS = () ARRAY = 0x0 FILL = -1 MAX = -1 ARYLEN = 0x0 FLAGS = (REAL) Elt No. 1 SV = PVLV(0x8387200) at 0x8359340 REFCNT = 2 FLAGS = (GMG,SMG) IV = 0 NV = 0 PV = 0 MAGIC = 0x839eee8 MG_VIRTUAL = &PL_vtbl_defelem MG_TYPE = PERL_MAGIC_defelem(y) TYPE = y TARGOFF = 1 TARGLEN = 1 TARG = 0x83693c0 SV = PVAV(0x835a228) at 0x83693c0 REFCNT = 3 FLAGS = () ARRAY = 0x0 FILL = -1 MAX = -1 ARYLEN = 0x0 FLAGS = (REAL) mysub SV = RV(0x83591fc) at 0x83591f0 REFCNT = 1 FLAGS = (TEMP,ROK) RV = 0x83696f0 SV = PVAV(0x835a23c) at 0x83696f0 REFCNT = 3 FLAGS = () ARRAY = 0x83a5f30 FILL = -1 MAX = 3 ARYLEN = 0x0 FLAGS = (REAL)

    Note in the above that the array @ARGV is located at 0x83693c0 ( SV = PVAV(0x835a228) at 0x83693c0 ). It has no elements (ARYLEN = 0x0).

    In the first call to mysub() there are two arguments: elements 0 and 1 or @ARGV. But the array @_ is not a simple array of values (see SV = PVLV(0x83871d4) at 0x83591f0 and following). The array has two elements and each of these have MAGIC and a reference to the @ARGV array. The first element of @_ has TARGOFF 0 and TARGLEN 1. These are offest and length into the array SV = PVAV(0x835a228) at 0x83693c0, which is @ARGV, which still has no elements (ARYLEN = 0x0). The second element is similar to the first but TARGOFF is 1, for element 1 of @ARGV instead of element 0.

    So, when you access $_[0], through the magic of the @_ array you get the value of $ARGV[0] and when you access $_[1] you get the value of $ARGV[1], but when you evaluate @_ in a scalar context you get 2 (the size of the @_ array) rather than 0 (the size of the @ARGV array). In this example the @_ array really does have two elements. In your script it really did have one element.

    The second call to mysub() has @ARGV as its argument list. This array is flattened and, as it has no elements in it, it is flattened into an empty list. Thus in the second call @_ has no elements.

    You might wonder, if perl passes arguments by reference, why, in the second call to mysub(), @_ isn't a reference to @ARGV (i.e. accessing @_ is the same as accessing @ARGV). I wasn't party to the decisions about how this works, so I can't tell you why. I can only tell you that in this case @_ is not the same as @ARGV, other than in the sense that they are both empty.

Re: Question: Is undef a valid argument?
by ig (Vicar) on May 06, 2009 at 23:58 UTC
    How else would you (recommend) check for passed arguments to be present if this does not work?

    I would either pass the content of @ARGV or a reference to it myself:

    use strict; use warnings; mysub1(@ARGV); mysub2(\@ARGV); exit(0); sub mysub1 { my @args = @_; print "There were " . scalar(@args) . " arguments: @args\n"; } sub mysub2 { my ($args) = @_; print "There were " . scalar(@$args) . " arguments: @$args\n"; }
Re: Question: Is undef a valid argument?
by repellent (Priest) on May 07, 2009 at 18:14 UTC