[comp.lang.perl] perl bug with $1, affected by subroutine call

jbw@bucsf.bu.edu (Joe Wells) (03/06/90)

When the this short perl program is run:

    &one(&sub1);
    &sub1;

    sub one { 1; }
    sub sub1 {
      if ('x' =~ m/(x)/)
	{ print "sub1 1: $1\n"; }
    }

I get this as output:

    sub1 1: 
    sub1 1: x

Would anyone happen to know why $1 is not getting set properly in
&one(&sub1)?

I've tried these variations, and they all work correctly:

    sub sub2 {
      if (('x' =~ m/(x)/) && 1)
	{ print "sub2 1: $1\n"; }
    }
    sub sub3 {
      if ('x' =~ m/(x)/)
	{ print "sub3 1: $1\n"; }
      1;
    }
    sub sub4 {
      'x' =~ m/(x)/;
      print "sub4 1: $1\n";
    }

Thanks!

-- 
Joe Wells <jbw@bu.edu>
jbw%bucsf.bu.edu@cs.bu.edu
...!harvard!bu-cs!bucsf!jbw

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (03/07/90)

In article <JBW.90Mar5215526@bucsf.bu.edu> jbw@bucsf.bu.edu (Joe Wells) writes:
: When the this short perl program is run:
: 
:     &one(&sub1);
:     &sub1;
: 
:     sub one { 1; }
:     sub sub1 {
:       if ('x' =~ m/(x)/)
: 	{ print "sub1 1: $1\n"; }
:     }
: 
: I get this as output:
: 
:     sub1 1: 
:     sub1 1: x
: 
: Would anyone happen to know why $1 is not getting set properly in
: &one(&sub1)?

Yes.  It's because in the first case you're calling sub1 in an array
context, and in the second, a scalar context.  Since the "if" is the
last statement of the sub block, it has to be evaluated in the
context that the subroutine was called.  So the /(x)/ has it's array
context behavior.  Unfortunately, the subroutine then goes on to
execute another "last" statement.  It's something like a halting problem.

: I've tried these variations, and they all work correctly:
: 
:     sub sub2 {
:       if (('x' =~ m/(x)/) && 1)
: 	{ print "sub2 1: $1\n"; }
:     }

This works because you've forced the context to scalar by embedding in &&.

:     sub sub3 {
:       if ('x' =~ m/(x)/)
: 	{ print "sub3 1: $1\n"; }
:       1;
:     }
:     sub sub4 {
:       'x' =~ m/(x)/;
:       print "sub4 1: $1\n";
:     }

These work because the pattern match is no longer potentially in the last
statement.

I'm not sure what the moral of the story is.  Certainly it would have been
possible to design a less confusing return mechanism had I known at the
beginning that subroutines would be returning array values.  But that didn't
come in till version 3.

I suppose it would be possible to force conditional expressions to scalar
context.  Hmmm.  I'll have to think about that...

Meanwhile, if you want to be sure, just put an explicit return value (not
to be confused with an explicit return).

Just another organic Perl grower,
Larry

jbw@bucsf.bu.edu (Joe Wells) (03/07/90)

In article <7291@jpl-devvax.JPL.NASA.GOV> lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:

   In article <JBW.90Mar5215526@bucsf.bu.edu> jbw@bucsf.bu.edu (Joe Wells) writes:
   : When the this short perl program is run:
   : 
   :     &one(&sub1);
   :     &sub1;
   : 
   : [stuff deleted]
   :
   : Would anyone happen to know why $1 is not getting set properly in
   : &one(&sub1)?

   Yes.  It's because in the first case you're calling sub1 in an array
   context, and in the second, a scalar context.

Huh?  Why is "&sub1" in "&one(&sub1)" being evaluated in an array context?
I intended it to be the single scalar argument to "&one".

   Since the "if" is the last statement of the sub block, it has to be
   evaluated in the context that the subroutine was called.

Huh?  Does this mean that an "if" statement has a value, unlike in C?  If
so, is there a precise specification of the value of an "if" statement?  I
can't find that in the manual.

If an "if (EXPR) BLOCK" statement is evaluated in a certain context, does
that mean that EXPR is always evaluated in the same context?  It seems
that EXPR should always be evaluated in a scalar context, and the value of
the "if" statement should be the value of BLOCK (evaluated in the
appropriate context).

   So the /(x)/ has it's array context behavior.

Question about this.  Consider this statement:

    @a = /xxxxxxx/;

What is the value of @a if the match succeeds?  (), right?

What is the value of @a if the match fails?  (), right?

Confused in Boston,

-- 
Joe Wells <jbw@bu.edu>

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (03/07/90)

In article <JBW.90Mar6151520@bucsf.bu.edu> jbw@bucsf.bu.edu (Joe Wells) writes:
: Huh?  Why is "&sub1" in "&one(&sub1)" being evaluated in an array context?
: I intended it to be the single scalar argument to "&one".

You can intend all you want--the single item passed to a subroutine call is
a LIST, which can have any number of arguments.  If you want a version
of Perl that treats 1 argument differently from 0 arguments or 2 arguments,
you can go back to Perl version 1.0, which made the distinction.  It was a
headache.  Perl now believes that lists can have 1 element.

:    Since the "if" is the last statement of the sub block, it has to be
:    evaluated in the context that the subroutine was called.
: 
: Huh?  Does this mean that an "if" statement has a value, unlike in C?  If
: so, is there a precise specification of the value of an "if" statement?  I
: can't find that in the manual.

If you look up subroutines you find:

     do SUBROUTINE (LIST)
             Executes a SUBROUTINE declared by a sub declaration,
             and returns the value of the last expression
             evaluated in SUBROUTINE.

What was the last expression evaluated in your subroutine?

: If an "if (EXPR) BLOCK" statement is evaluated in a certain context, does
: that mean that EXPR is always evaluated in the same context?  It seems
: that EXPR should always be evaluated in a scalar context, and the value of
: the "if" statement should be the value of BLOCK (evaluated in the
: appropriate context).

That's precisely what I was suggesting as a possible remedy in my article.

:    So the /(x)/ has it's array context behavior.
: 
: Question about this.  Consider this statement:
: 
:     @a = /xxxxxxx/;
: 
: What is the value of @a if the match succeeds?  (), right?
: 
: What is the value of @a if the match fails?  (), right?

At this point the documentation lies slightly.  It implies that
you'd get a null array either way.  In fact, it only pays attention
to the array context if there are any parens or if it fails, so you'd get
an array with a value of (1) if it matches, and () if it doesn't.
Kind weird.

I will fix the man page.  Thanks.

chdir '/usr/spool/news/comp/lang/perl' || die "Not a \"normal\" news server.\n";
sub bynum {$a - $b;} $/ = '\0'; $* = 1; $| = 1;
opendir(DIR,'.'); @arts = sort bynum readdir(DIR);
srand(time); print "Not ";
while ($art = $arts[rand @arts]) {
    open art; $_ = <art>; next unless s/\n-- *\n.*Randal [^\0]*/\n/;
    s/[^\0]*\n\n//; next unless $_; eval $_; last unless $@;
}

Larry

jbw@bucsf.bu.edu (Joe Wells) (03/07/90)

In article <7298@jpl-devvax.JPL.NASA.GOV> lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:

   In article <JBW.90Mar6151520@bucsf.bu.edu> jbw@bucsf.bu.edu (Joe Wells) writes:
   : Huh?  Why is "&sub1" in "&one(&sub1)" being evaluated in an array context?
   : I intended it to be the single scalar argument to "&one".

   You can intend all you want--the single item passed to a subroutine call is
   a LIST, which can have any number of arguments.
     ^^^^

Ahh, things become clear!

Ok.  Let me rephrase what you've said and see if you disagree with it.

First, the syntax for a subroutine call is like this:

do SUB ( LIST )
& SUB ( LIST )
& SUB

Second, each item in a LIST is evaluated in an array context.  If that
item evaluates to an array, each item of the resulting array is added to
the list, otherwise the single resulting scalar item is added to the list.

Is this a correct restatement, or am I still missing something?

   :    Since the "if" is the last statement of the sub block, it has to be
   :    evaluated in the context that the subroutine was called.
   : 
   : Huh?  Does this mean that an "if" statement has a value, unlike in C?  If
   : so, is there a precise specification of the value of an "if" statement?  I
   : can't find that in the manual.

   If you look up subroutines you find:

	do SUBROUTINE (LIST)
		Executes a SUBROUTINE declared by a sub declaration,
		and returns the value of the last expression
		evaluated in SUBROUTINE.

   What was the last expression evaluated in your subroutine?

Not the conditional of the "if" statement!

-- 
Joe Wells <jbw@bu.edu>
jbw%bucsf.bu.edu@cs.bu.edu
...!harvard!bu-cs!bucsf!jbw

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (03/08/90)

In article <JBW.90Mar6190955@bucsf.bu.edu> jbw@bucsf.bu.edu (Joe Wells) writes:
: Ok.  Let me rephrase what you've said and see if you disagree with it.
: 
: First, the syntax for a subroutine call is like this:
: 
: do SUB ( LIST )
: & SUB ( LIST )
: & SUB
: 
: Second, each item in a LIST is evaluated in an array context.  If that
: item evaluates to an array, each item of the resulting array is added to
: the list, otherwise the single resulting scalar item is added to the list.
: 
: Is this a correct restatement, or am I still missing something?

You're right on the money.

:    :    Since the "if" is the last statement of the sub block, it has to be
:    :    evaluated in the context that the subroutine was called.
:    : 
:    : Huh?  Does this mean that an "if" statement has a value, unlike in C?  If
:    : so, is there a precise specification of the value of an "if" statement?  I
:    : can't find that in the manual.
: 
:    If you look up subroutines you find:
: 
: 	do SUBROUTINE (LIST)
: 		Executes a SUBROUTINE declared by a sub declaration,
: 		and returns the value of the last expression
: 		evaluated in SUBROUTINE.
: 
:    What was the last expression evaluated in your subroutine?
: 
: Not the conditional of the "if" statement!

Ok, you're right, it wasn't the last expression, since the conditional
turned out to be true.  But it would have been last if the conditional had
been false.  And there's no way to tell beforehand.  But you have to
supply the context to an expression before it gets executed.  Catch 22.

There's a possible problem with forcing the context of a conditional EXPR
to scalar.  If we force it, then when it doesn't match it will return
what looks to the subroutine as a non-null array value, i.e. ('').  So
if in calling the subroutine they say
	
	if (@retval = &mysub()) {

then they'll get a true indication from the array assignment, even though
the only value in the array is false.  Time to think s'more...

Larry