foreach my $item (@items) {
#process each item
} else {
#handle the empty list case
}
I find it a very nice addition as I've written code like this:
if (@stuff) {
for my $thing (@stuff) {
..
}
} else {
..
}
many times. An else block would save keystrokes and would make the code
more readable. Python also has it.
What do you think?
--
Thomas Wittek
http://gedankenkonstrukt.de/
Jabber: strea...@jabber.i-pobox.net
It will confuse the Python folks to no end (assuming they'd ever touch
anything resembling Perl). That is not what it means in Python. The
python else construct assumes you are looping in order to find
something:
for x in list:
if something(x):
print "Found"
break
else:
print "Not found"
That is, the else block is fired if you get to the end of the list
without breaking.
I'm not sure about either interpretation, but admittedly Python's is
harder to emulate clearly. I wonder if a closure trait should do the
same thing. Or I wonder if there's really a problem here that we're
addressing, or whether we're just adding syntax because we can.
Luke
I'm not sure if I like it, but there have been several times that I would've
used it recently. I think it would certainly have utility.
Paul
This syntax awkwardness comes up a lot in various kinds of reports.
for @records {
.print;
}
if @records {
say "Search returned no records";
}
I've always found it annoying that I have to write @records (or
whatever) twice. I don't know if Thomas's idea is the best possible but
it's not horrible. Hmm, I wonder if I could get it into Template
toolkit.
[% FOREACH records %]
<span>[% whatever %]</span>
[% ELSE %]
<span>Search returned no records.</span>
[% END %]
--
Rick Delaney
ri...@bort.ca
You trapped me. :) Actually I don't know any python but I've once read a
for/else construct in python. But obviously it doesn't dwIm.
From the Python Reference Manual:
When the items are exhausted (which is immediately when the sequence
is empty), the suite in the else clause, if present, is executed,
and the loop terminates.
> I'm not sure about either interpretation, but admittedly Python's is
> harder to emulate clearly.
I'd like the For::Else behaviour more. Especially as I remember numerous
times writing an if clause to check if a list is empty before processing it.
I don't remember many cases where I wrote something like this:
my $found;
foreach my $item (@items) {
if ($item = 'foobar') {
$found = 1;
last;
}
}
unless ($found) {
..
}
To make it more clear, I could imagine (a subset of) this:
for @items -> $item {
say $item;
if $item == 42 {
say "I've found the Answer to Life, the Universe, and Everything!";
last;
}
} start {
say "Mh, I'll look for 42!"
} end {
say "The end has been reached. 42 not found."
} empty {
say "No items."
Well, that's assuming you come at it only with the 'for' loop. But this
is still Perl, so there's still more than one way to do it. :-)
$result = first { .something }, @items
err fail "nothing found";
: I'd like the For::Else behaviour more. Especially as I remember numerous
: times writing an if clause to check if a list is empty before processing it.
:
: I don't remember many cases where I wrote something like this:
:
: my $found;
: foreach my $item (@items) {
: if ($item = 'foobar') {
: $found = 1;
: last;
: }
: }
: unless ($found) {
: ..
: }
If you actually wrote that, then you'll always find that the first
item has the value 'foobar'. :)
: To make it more clear, I could imagine (a subset of) this:
:
: for @items -> $item {
: say $item;
: if $item == 42 {
: say "I've found the Answer to Life, the Universe, and Everything!";
: last;
: }
: } start {
: say "Mh, I'll look for 42!"
: } end {
: say "The end has been reached. 42 not found."
: } empty {
: say "No items."
: }
Well, leaving out the extraneous stuff, here's both kinds of "else" in one:
$result = first 42, (@items || say "No items.")
err say "The end has been reached. 42 not found.";
To get all of it in, you could use something more like:
$result = first Any, gather for @items || say "No items." {
FIRST {
say "Mh, I'll look for 42!"
}
when 42 {
say "I've found the Answer to Life, the Universe, and Everything!";
take $_;
}
LAST {
say "The end has been reached. 42 not found.";
}
}
That's assuming the LAST never runs because the gather never requests
a second take. (And also assuming that when the loop is GC'd it also
doesn't run LAST.) Note that in either case we're using smartmatch,
so you're not going to make the =/== mistake.
Perhaps there should be something like the "first gather" that does
the gather assuming only one thing will be gathered. Unfortunately
"item gather" will still gather everything and make it [list].
Larry
== of course ;)
> If you actually wrote that, then you'll always find that the first
> item has the value 'foobar'. :)
> Well, leaving out the extraneous stuff, here's both kinds of "else" in one:
>
> $result = first 42, (@items || say "No items.")
> err say "The end has been reached. 42 not found.";
That's a solution to find the first matching item.
But that's not the only reason to loop through a list.
"else" would be a general purpose solution.
> To get all of it in, you could use something more like:
>
> $result = first Any, gather for @items || say "No items." {
> FIRST {
> say "Mh, I'll look for 42!"
> }
> when 42 {
> say "I've found the Answer to Life, the Universe, and Everything!";
> take $_;
> }
> LAST {
> say "The end has been reached. 42 not found.";
> }
> }
That's, well, elegant! Yes.
Because and but it's tricky.
Nothing where I'd say "wow, thats an easy solution to my problem!".
It's a bit complicated, because you have to understand and combine
several concepts. That's elegant. But not easy, I think.
I think it's not simple enough for this simple kind of problem.
Care to try a third time?
I don't think the numeric-casting == will do what you want either.
-- Darren Duncan
Or how about eq? :)
--
korajn salutojn,
juerd waalboer: perl hacker <ju...@juerd.nl> <http://juerd.nl/sig>
convolution: ict solutions and consultancy <sa...@convolution.nl>
Ik vertrouw stemcomputers niet.
Zie <http://www.wijvertrouwenstemcomputersniet.nl/>.
exactly, my full support to thomas with this, perl was always about
keep simple things simple too.
herbert breunung
proton-ce.sf.net
_____________________________________________________________________
Der WEB.DE SmartSurfer hilft bis zu 70% Ihrer Onlinekosten zu sparen!
http://smartsurfer.web.de/?mc=100071&distributionid=000000000066
Seconded. I would favor allowing an "else" block to be attached
following any loop block, with the semantics being that the else block
only gets triggered if the loop block doesn't run at least once. I'd
do this instead of a block trait (such as FIRST or LAST) because of
the either-or relationship between the loop block and the else block.
--
Jonathan "Dataweaver" Lang
The one exception would be the "repeat" statement, because attaching
an "else" block to it would be as confusing semantically as attaching
an "else" block to an "unless" statement.
--
Jonathan "Dataweaver" Lang
Shame on me. I confused my examples. Read the 42 thing and wrote above.
Must be > of course, to leave the gag running ;)
Next time I should write my messages with "use warnings;"...
That's crazy. If the list is empty foreach still does the right thing
- there's no benefit in guarding a foreach with a conditional.
> I don't remember many cases where I wrote something like this:
>
> my $found;
> foreach my $item (@items) {
> if ($item = 'foobar') {
> $found = 1;
> last;
> }
> }
> unless ($found) {
> ..
> }
I'd say that or a close variation of it was a pretty common idiom.
--
Andy Armstrong, hexten.net
More exceptional rules in a language are bad in itself.
Those exceptions force people to more to learn more stuff
and lead to confusion for those who don't know every detail
of this language. So, there should be an important reason
for that or it's a silly idea.
I think your proposal is conflicting with the common meaning
of 'else' and I fear that this would discourage many
professional programmers that are used to other languages,
to give perl6 a try. In other words: Features are good but
breaking conventions is bad.
On the other hand, there is no important reason for it because C<
for @rray -> $el {}
if ! @rray {}
>
should work. It's short and easy to understand.
So, I think there is no problem at all with the
currently specced syntax.
Kind Regards
Stefan
---------------------------------
It's here! Your new message!
Get new email alerts with the free Yahoo! Toolbar.
given @foo {
for $_ -> $x { ... }
when .empty { ... }
}
You can reverse the order if you want:
given @foo {
when .empty { ... }
for $_ -> $x { ... }
}
I don't like C<$_>, but I can't think of a way to get rid of it.
--
Daniel Brockman <dan...@brockman.se>
Actually, you'd be better off using the second order; the "when"
statement would drop you out of the "given" block if triggered, and
you'd never get to the loop. In the first order, you'd be triggering
both the loop and the "when" every time the "given" block executes.
--
Jonathan "Dataweaver" Lang
> On 3 Mar 2007, at 00:39, Thomas Wittek wrote:
>
> > I'd like the For::Else behaviour more. Especially as I remember
> > numerous times writing an if clause to check if a list is empty
> > before processing it.
>
> That's crazy. If the list is empty foreach still does the right thing
> - there's no benefit in guarding a foreach with a conditional.
The purpose of the C<if> test is not to guard the loop but to attach an
C<else> clause to (hence the above mention of C<For::Else>), so that
some code can be run only in the case where the list is empty.
I have many times wanted this: either list the results or display a
message saying that there aren't any results; process each invoice, or
throw an error complaining there aren't any invoices.
Oooh, "or", now there's an idea. In Perl 6 the return value of C<for>
is the list of values that successfully completed an iteration. In the
case of their not being any items in the list to start with that will be
an empty list, which is false. So I think this will work:
for @invoice
{
.process;
} or fail 'No invoices to process';
> > I don't remember many cases where I wrote something like this:
> >
> > my $found;
> > foreach my $item (@items) {
> > if ($item = 'foobar') {
> > $found = 1;
> > last;
> > }
> > }
> > unless ($found) {
> > ..
> > }
>
> I'd say that or a close variation of it was a pretty common idiom.
I've many times wanted a better way of doing that too. Basically you
want an C<else> to attach to the C<if> but only be activated if none of
the. Larry's suggestion of using C<first> for this looks good.
Smylers
I guess that most people would understand the for/empty(/start/end) code
without having ever written any line of Perl.
They won't understand first/Any/gather directly, I think.
> On the other hand, there is no important reason for it because C<
>
> for @rray -> $el {}
> if ! @rray {}
>
> should work. It's short and easy to understand.
Agree, this looks good indeed.
> On the other hand, there is no important reason for it because C<
>
> for @rray -> $el {}
> if ! @rray {}
>
> >
> should work. It's short and easy to understand.
But it involves repeating C<@rray> -- which for more complex expressions
(results from function calls, delving deep into very nested data
structures, whatever) could be tedious. Repetition is certainly poor
style and makes the code more prone to errors being introduced.
Also, it relies on the contents of C<@rray> not being cleared inside the
loop.
Smylers
If that actually works then I'm happy.
--
Rick Delaney
ri...@bort.ca
It's dependent on .process not returning a false on the final iteration.
--
Jonathan "Dataweaver" Lang
Er, these days 'for' is more like 'map', and hence returns a list.
So it's dependent on at least one iteration returning a non-() value.
In fact, if the final iteration returned False, the list would be
considered true.
Larry
The situation the thread is referring to has @invoice empty so no
iterations are taking place and all this is a non-issue. :)
--
Tom Lanyon
You would still get a false positive on a non-null list if all of
the iterations return ().
Larry
I stand corrected. Is there a way to avoid the false positive case?
Sounds like the following will work, but it doesn't seem 'nice'.
for @invoice
{
.process;
1;
} or fail 'No invoices to process';
--
Tom Lanyon
Still think if there's no invoices it logically should be tested first.
If you don't want to repeat mentioning the array, how 'bout:
@invoice or fail 'No invoices to process'
==> for @() {
.process
}
or equivalently
@invoice or fail 'No invoices to process'
==> map {
.process
}
all assuming you don't like my original
for @invoice || fail 'No invoices to process'
{
.process
}
Larry
Ah, I missed that last one when you originally posted it.
All three of those are quite sufficient solutions in my mind.
p.s. Could the latter 'for @foo || fail { }' cause confusion with 'for
@foo {} or fail' ?
--
Tom Lanyon
These should work, as long as you don't replace "fail 'no invoices to
process'" with something that returns a non-empty list.
That said, you could:
{
for =<> || fail 'no input'
{
.process
}
CATCH {
do_something_else if 'no input'
}
}
...where the 'no input' string is there only so that failures
generated by .process will propagate correctly.
--
This solution isn't generalizable beyond "for", though: you wouldn't
be able to use it for "while, "until", or "loop", all of which could
potentially follow the logic of "do something special if the loop
fails to run even once". Hmm...
while test -> $result {
do_something;
FIRST { do_something_else unless $result }
}
...except that I don't think that FIRST would run unless $result is true.
I suppose that you _could_ write:
if test {
repeat {
do_something
} while test
} else {
do_something_else
}
You'd be having to spell out the test twice, though.
--
Jonathan "Dataweaver" Lang