[comp.lang.perl] Add 'continue' to 'foreach'?

bjaspan@ATHENA.MIT.EDU (Barr3y Jaspan) (05/08/91)

I have a perl program in which I am doing a select() on a large number
of sockets and then iterating over those that get set.  Initially, I
was doing this

	for ($i=0; $i < $maxfn; $i++) {
		next unless vec($rdset, $i, 1);

		# stuff
	}

where $maxfn happens to be the largest filedescriptor number in
$rdset.  Since vec() is slow, I am now doing something like this (note
that I have to maintain $i in any event, because I have several arrays
keyed on it):

	@rdset = split(//, unpack('b*', $rdset));
	$i = 0;
	foreach (@rdset) {
		if (! $_) {
			$i++;
			next;
		}

		# stuff
		$i++;
	}

The block attached to the 'if' is ugly, and this is perl, so there
should be a magic way around the problem.  (Also, in a recent posting,
someone said that having a block like that is slower than doing it
with a postfix if/unless.  Clearly, if there is a local() inside the
block there will be overhead; does Perl not optimize if there is no
local()?)  

How about adding an optional continue block to the foreach statement?
Then I could do something like this:

	$i = 0
	foreach (@rdset) {
		next unless $_;

		# stuff
	} continue {
		$i++;
	}

The advantage to doing it this way is that foreach is non-destructive.
Doing it with a while (which has a continue block) would require
shifting the array, which (1) would mean I couldn't use it again, or I
would have to keep a copy, and (2) I would guess is slower than just
iterating over the elements.

-----------

As a separate issue, I just thought of another way to do it (after
all, this is perl):

	$rdstr = unpack('b*', $rdset);
	$i = length($rdstr);
	while (chop $rdstr) {
		next unless $_;
		# stuff
	} continue { $i--; }

This saves a split(), and adds a length() and a bunch of chop()s; it
also iterates backwards (although this doesn't matter in this
particular case).  Is this faster?  (ie: how fast is chop?)

$_ = "\n,rekcah lrep rehtona tsuJ"; print chop while $_;

Barr3y Jaspan, bjaspan@mit.edu

flee@cs.psu.edu (Felix Lee) (05/08/91)

>How about adding an optional continue block to the foreach statement?

Guess what.  It's already there.

You can add continue blocks to any sort of loop, and even "if"
statements, although I'm not sure what that means.

You can also add "else" and "elsif" statements to any sort of loop,
though I'm even less sure what that means.  It seems to elicit panics.
--
Felix Lee	flee@cs.psu.edu

lwall@jpl-devvax.jpl.nasa.gov (Larry Wall) (05/09/91)

In article <1991May8.015608.19707@uvaarpa.Virginia.EDU> bjaspan@ATHENA.MIT.EDU writes:
: How about adding an optional continue block to the foreach statement?
: Then I could do something like this:
: 
: 	$i = 0
: 	foreach (@rdset) {
: 		next unless $_;
: 
: 		# stuff
: 	} continue {
: 		$i++;
: 	}
: 
: The advantage to doing it this way is that foreach is non-destructive.
: Doing it with a while (which has a continue block) would require
: shifting the array, which (1) would mean I couldn't use it again, or I
: would have to keep a copy, and (2) I would guess is slower than just
: iterating over the elements.

Before asking for a new feature, you might check to see if it's already
there.  :-)  I just said:

    foreach (1..10) {
        print "$i\n" unless $_ & 1;
    }
    continue {
        $i++;
    }

and it said

    1
    3
    5
    9

: As a separate issue, I just thought of another way to do it (after
: all, this is perl):
: 
: 	$rdstr = unpack('b*', $rdset);
: 	$i = length($rdstr);
: 	while (chop $rdstr) {
: 		next unless $_;
: 		# stuff
: 	} continue { $i--; }
: 
: This saves a split(), and adds a length() and a bunch of chop()s; it
: also iterates backwards (although this doesn't matter in this
: particular case).  Is this faster?  (ie: how fast is chop?)

I suspect it's faster, but Perl programming is an empirical science.
(Actually, I suspect that it doesn't work, since "while (chop $rdstr)"
is not going to assign to $_.  Say "while ($_ = chop $rdstr)" instead.
(Actually, that won't work either, since "0" is false.))

The chop operator itself is quite fast.  I'd write it like this if I
wanted to use chop:

    while ($rdstr ne '') {
	next unless chop $rdstr;
	# stuff
    }
    continue {
	$i--;
    }

Howsomever, I wouldn't use chop, especially when most of the bits are going
to be 0.  I'd say

    for ($fd = index($rdstr,'1'); $fd >= 0; $fd = index($rdstr,'1',$fd+1)) {
	# stuff
    }

It's almost always a mistake to write a linear search algorithm in Perl.
Let Perl do the searching for you.

Larry