Google Группы больше не поддерживают новые публикации и подписки в сети Usenet. Опубликованный ранее контент останется доступен.

Why can a next within subroutine influence code outside the s ubroutine?

0 просмотров
Перейти к первому непрочитанному сообщению

Mike Giroux

не прочитано,
28 февр. 2000 г., 03:00:0028.02.2000
Stephen Gertsch <sger...@slip.net> wrote:
> I do not understand why perl allows the use of 'next' outside
> the context
> block to which it pertains. Consider the following code fragment:
>
> while($i++<5)
> {
> print "Before sub call i=$i\n";
> &XXX;
> print "After sub call i=$i\n";
> }
>
> sub XXX
> {
> next;
> }
>
> It appears to me that perl subroutines behave more like macros than
> subroutines. If it is not already obvious, I am new to perl
> and feel that I
> am missing an important philosophical point. Would someone
> care to enlighten
> me?
>


The quick summary of this long post: I don't know why it happens, it
certainly
happens, it's dangerous, and always use -w :)


The long version:

I couldn't believe this post, so I had to play around with this. Sure
enough,
Stephen is right.

Using this test code:
#!/usr/local/bin/perl

while($i++<5)
{
print "Before sub call i=$i\n";
&XXX;
print "After sub call i=$i\n";
}

sub XXX
{
next;
}

Here's what we get:
~$ perl -w test_goto.pl
Before sub call i=1
Exiting subroutine via next at test_goto.pl line 12.
(4 more times, incrementing i)

The Camel (2nd Edition) is no help; it just says (pg 573)
that

(W) You are exiting a subroutine by unconventional means, such as a goto,
or a loop control statement.

What stuns me is that the next actually works. So I'm guessing that a
misplaced next
in a module or nested sub could really mess you up. Testing the concept:


#!/usr/local/bin/perl

use strict;

sub XXX
{
print "\tbefore next in sub\n";
next;
print "\tafter next in sub\n";
}

sub XX1
{
my $sub='XX1';
print "$sub in\n";
XXX;
print "$sub out\n";
}

sub XX2
{
my $sub='XX2';
print "$sub in\n";
XX1;
print "$sub out\n";
}

my $i;

while($i++<5)
{
print "Before sub call i=$i\n";
XX2;
print "After sub call i=$i\n";
}

Running this confirms that yes, the next in XXX exits all the way back to
the loop :((

Putting 'package A;' before XXX and 'package main;' after it shows that
this'll work
even from a package.

"Happily", calling XX2 outside of a loop gets you this error:

Can't "next" outside a block at test_goto.pl line 10.

even if -w isn't used.

Otherwise, you're not going to see any warning at all unless you use -w.

Ugly.
--
Mike

Chris Hostetter (The Hoss Man)

не прочитано,
28 февр. 2000 г., 03:00:0028.02.2000

Mike Giroux wrote:

> The Camel (2nd Edition) is no help; it just says (pg 573)
> that
>
> (W) You are exiting a subroutine by unconventional means, such as a goto,
> or a loop control statement.
>
> What stuns me is that the next actually works. So I'm guessing that a
> misplaced next
> in a module or nested sub could really mess you up. Testing the concept:

Acctually, this behaviour is well documented in the man pages...

legoland:~> perl -v

This is perl, version 5.004_04 built for sun4-solaris

Copyright 1987-1997, Larry Wall

Perl may be copied only under the terms of either the Artistic License
or the
GNU General Public License, which may be found in the Perl 5.0 source
kit.

legoland:~> perldoc perlsyn | grep -3 "innermost enclosing loop"

the loop for the loop control statements next, last, and
redo. If the LABEL is omitted, the loop control statement
refers to the innermost enclosing loop. This may include
dynamically looking back your call-stack at run time to find
the LABEL. Such desperate behavior triggers a warning if
you use the -w flag.

Martien Verbruggen

не прочитано,
29 февр. 2000 г., 03:00:0029.02.2000
On Mon, 28 Feb 2000 09:18:15 -0500,
Mike Giroux <rmgi...@jemmcop.com> wrote:
> Stephen Gertsch <sger...@slip.net> wrote:
>
> > I do not understand why perl allows the use of 'next' outside the
> > context block to which it pertains. Consider the following code
> > fragment:
>
> The quick summary of this long post: I don't know why it happens, it
> certainly happens, it's dangerous, and always use -w :)
[snip]
> Ugly.

[See also my other post]

Ugly, yes. But I really think it doesn't matter. By doing this, you do
something that is documented not to work, without defining what will
happen if you do it anyway. In this particular version of perl, the
next propagates up, or something like that. The next one may fail with
a fatal error. Another one may cause your computer to sprout wings and
fly out of the window.

It's just as ugly as what may happen if you do something undefined in
any other programming language.

Martien
--
Martien Verbruggen |
Interactive Media Division | 42.6% of statistics is made up on the
Commercial Dynamics Pty. Ltd. | spot.
NSW, Australia |

Martien Verbruggen

не прочитано,
29 февр. 2000 г., 03:00:0029.02.2000
On Mon, 28 Feb 2000 14:35:39 -0800,
Chris Hostetter (The Hoss Man) <nos...@dont.spam.me.or.ill.be.mad.us> wrote:
>
> Acctually, this behaviour is well documented in the man pages...
>
> legoland:~> perl -v
>
> This is perl, version 5.004_04 built for sun4-solaris
>
> Copyright 1987-1997, Larry Wall
>
> Perl may be copied only under the terms of either the Artistic License
> or the
> GNU General Public License, which may be found in the Perl 5.0 source
> kit.
>
> legoland:~> perldoc perlsyn | grep -3 "innermost enclosing loop"
>
> the loop for the loop control statements next, last, and
> redo. If the LABEL is omitted, the loop control statement
> refers to the innermost enclosing loop. This may include
> dynamically looking back your call-stack at run time to find
> the LABEL. Such desperate behavior triggers a warning if
> you use the -w flag.

That just documents it for use in loop control statements. It does not
document it for use in a sub block. It may very well be, and probably
is, what is happening, but you can't say it is documented to work that
way. the C<next> entry in the perlfunc man page explicitly states that
you cannot use it in a block that returns a value.

Martien
--
Martien Verbruggen |
Interactive Media Division | Make it idiot proof and someone will
Commercial Dynamics Pty. Ltd. | make a better idiot.
NSW, Australia |

Neko

не прочитано,
29 февр. 2000 г., 03:00:0029.02.2000
On Tue, 29 Feb 2000 01:56:58 GMT, mg...@verbruggen.comdyn.com.au (Martien
Verbruggen) wrote:

>On Mon, 28 Feb 2000 14:35:39 -0800,
> Chris Hostetter (The Hoss Man) <nos...@dont.spam.me.or.ill.be.mad.us> wrote:
>>
>> Acctually, this behaviour is well documented in the man pages...
>>

>> legoland:~> perldoc perlsyn | grep -3 "innermost enclosing loop"
>>
>> the loop for the loop control statements next, last, and
>> redo. If the LABEL is omitted, the loop control statement
>> refers to the innermost enclosing loop. This may include
>> dynamically looking back your call-stack at run time to find
>> the LABEL. Such desperate behavior triggers a warning if
>> you use the -w flag.
>
>That just documents it for use in loop control statements. It does not
>document it for use in a sub block.

That *does* document the behavior for sub blocks. You get no warnings from -w
regardless of how many inner/outer loops there are.

>It may very well be, and probably
>is, what is happening, but you can't say it is documented to work that
>way. the C<next> entry in the perlfunc man page explicitly states that
>you cannot use it in a block that returns a value.

That paragraph (and similar ones for 'last' and 'redo') was added sometime after
Perl 5.005_01. From 5.005_3:

next cannot be used to exit a block which returns a value such as
eval {}, sub {} or do {}, and should not be used to exit a grep()
or map() operation.

It looks like the perlsyn documentation about loop control statements needs to
be amended. And the warnings in perldiag should be changed as well, from:

Exiting subroutine via %s



(W) You are exiting a subroutine by unconventional means, such as
a goto, or a loop control statement.

To something like:

(F) You cannot exit a subroutine through a loop control statement.

I omitted 'goto' in that hypothetical fatal warning because perlfunc does not
document exiting a subroutine throught 'goto' as a "cannot":

It may not be used to go into any construct that requires initialization,
such as a subroutine or a foreach loop. It also can't be used to go into a
construct that is optimized away, or to get out of a block or subroutine
given to sort(). It can be used to go almost anywhere else within the
dynamic scope, including out of subroutines, but it's usually better to use
some other construct such as last or die().

Hm... looks like the documentation for 'goto' need to be fixed as well. It's
actually recommending that you use 'last' instead of 'goto' to get out of a
subroutine.

--
Neko

Mark-Jason Dominus

не прочитано,
1 мар. 2000 г., 03:00:0001.03.2000

On Tue, 29 Feb 2000 01:56:58 GMT, mg...@verbruggen.comdyn.com.au (Martien
Verbruggen) wrote:
> >> the loop for the loop control statements next, last, and
> >> redo. If the LABEL is omitted, the loop control statement
> >> refers to the innermost enclosing loop. This may include
> >> dynamically looking back your call-stack at run time to find
> >> the LABEL. Such desperate behavior triggers a warning if
> >> you use the -w flag.
> >
> >That just documents it for use in loop control statements. It does not
> >document it for use in a sub block.

No, `loop control statement' here refers to `next', `last', and
`redo', not to `for' or `while'. So the meaning of this:

If the LABEL is omitted, the loop control statement refers to

the innermost enclosing loop. ...

is actually:

If the LABEL is omitted, the `next', `last', and `redo'
operators refer to the innermost enclosing loop. This may


include dynamically looking back your call-stack at run time

to find [one].

t...@chocobo.org (Neko) said:
> That *does* document the behavior for sub blocks. You get no
> warnings from -w regardless of how many inner/outer loops there are.

Sure you do. Try this:

sub fred { last }
for (1 .. 20) { $n = $_; fred() if $_ == 11 }
print "$n\n";

You get this warning:
Exiting subroutine via last at /tmp/lb.pl line 1.

And the value of $n that is printed is:
11

Which shows that the `next' in the subroutine *does* look back up the
call stack and exit the outer `for' loop, and you *do* get a warning
from it. And also, fred() never gets the opportunity to return a
value, because you jumped out of it and clear out of the context in
which it was called; if you replace fred() above with $g = fred(),
$g remains undefined after fred() `returns'.

> It looks like the perlsyn documentation about loop control
> statements needs to be amended.

They seem accurate to me.

> And the warnings in perldiag should be changed as well, from:
>
> Exiting subroutine via %s
>
> (W) You are exiting a subroutine by unconventional means, such as
> a goto, or a loop control statement.
>
> To something like:
>
> (F) You cannot exit a subroutine through a loop control statement.

No, certainly not, because you can exit a subroutine through a loop
control statement, and it's not a fatal error. Please remember that
the phrase `loop control statement' in the existing error message
refers to `next', `last', or `redo', so what it really means is:

Exiting subroutine via %s

(W) You are exiting a subroutine by unconventional means, such as

a goto, or a a next, last, or redo statement.

> I omitted 'goto' in that hypothetical fatal warning because perlfunc does not
> document exiting a subroutine throught 'goto' as a "cannot":

The `cannot' in the description ihas misled you. What that is
addressing is a common question involving grep and map. here is an
example of the question:

A: ``I have some arrays of numbers and I want to see which ones
contain ant numbers that are bigger than 100. I am going to
use this code:''

for $a (@arrays) {
if (grep {$_ > 100} @$a) { ... }
}

B: ``That is a bad thing to do, because the `grep' will search
all the way to the end, even if there is a large number at the
beginning of the array, and that is a waste of time.''

A: ``In that case I will tell `grep' to stop as
soon as it sees a large number:''

for $a (@arrays) {
if (grep {$_ > 100 and last} @$a) { ... }
}

But `last' doesn't escape from `grep'. Or rather, it *does*, but it
also escapes the enclosing `for'. The `cannot' in the manual means
`it does not do what you want', rather than `it is forbidden'. If
what you you really want to jump clear out of the smallest loop that
*encloses* the grep, it works just fine.

> It's actually recommending that you use 'last' instead of 'goto' to
> get out of a subroutine.

No, it's recommending that you avoid non-local transfer of control
entirely, and stick to the more structured and constrained forms like
`last' where possible.

Neko

не прочитано,
1 мар. 2000 г., 03:00:0001.03.2000
On Wed, 01 Mar 2000 02:20:47 -0500, Mark-Jason Dominus <m...@plover.com> wrote:

>
>On Tue, 29 Feb 2000 01:56:58 GMT, mg...@verbruggen.comdyn.com.au (Martien
>Verbruggen) wrote:
>> >> the loop for the loop control statements next, last, and
>> >> redo. If the LABEL is omitted, the loop control statement
>> >> refers to the innermost enclosing loop. This may include
>> >> dynamically looking back your call-stack at run time to find
>> >> the LABEL. Such desperate behavior triggers a warning if
>> >> you use the -w flag.
>> >
>> >That just documents it for use in loop control statements. It does not
>> >document it for use in a sub block.
>

>t...@chocobo.org (Neko) said:
>> That *does* document the behavior for sub blocks. You get no
>> warnings from -w regardless of how many inner/outer loops there are.
>
>Sure you do. Try this:
>
> sub fred { last }
> for (1 .. 20) { $n = $_; fred() if $_ == 11 }
> print "$n\n";
>
>You get this warning:
> Exiting subroutine via last at /tmp/lb.pl line 1.

Let me rephrase that: You get no warnings from -w *because* of how many
inner/outer loops there are. The warnings are generated from exiting the
subroutine. Hence, the documentation *does* document the behavior for sub
blocks.

>> It looks like the perlsyn documentation about loop control


>> statements needs to be amended.
>
>They seem accurate to me.

They seemed accurate to me also. But then the documentation for 'next', 'last',
and 'redo' rather unambiguously state that they cannot be used to exit a
subroutine. From perlfunc:

next cannot be used to exit a block which returns a value such
as eval {}, sub {} or do {}, and should not be used to exit a
grep() or map() operation.

>> I omitted 'goto' in that hypothetical fatal warning because perlfunc does not


>> document exiting a subroutine throught 'goto' as a "cannot":
>
>The `cannot' in the description ihas misled you. What that is
>addressing is a common question involving grep and map. here is an
>example of the question:

The "cannot" in the description clearly refers to eval/sub/do whereas the
"should not" applies to grep/map. Thank you for explaining why next/last/redo
should not be used to exit the latter two constructs.

Perhaps I am reading the documentation too literally. Perhaps it meant to say
this instead:

next cannot be *usefully* used to exit a block which returns a value such


as eval {}, sub {} or do {}, and should not be used to exit a
grep() or map() operation.

>> It's actually recommending that you use 'last' instead of 'goto' to


>> get out of a subroutine.
>
>No, it's recommending that you avoid non-local transfer of control
>entirely, and stick to the more structured and constrained forms like
>`last' where possible.

That's what I said. :)

--
Neko

0 новых сообщений