last iteration of a for loop

G

gamo

El 27/02/14 12:11, Ted Zlatanov escribió:
my @list = sort keys %ENV;

while (my $key = pop @list)
{
print "$key\n";
print "not last\n" if scalar @list;
print "last\n" unless scalar @list;
}


I miss in this discusion a solution using when/default.
Can not be as efficient as using if?
 
R

Rainer Weikusat

gamo said:
El 27/02/14 12:11, Ted Zlatanov escribió:


I miss in this discusion a solution using when/default.
Can not be as efficient as using if?

As contorted because it is just another way of putting
some code into the loop body which simply doesn't belong in there,
hence, execution of it has to be disabled by 'hitting the loop on the
head with a blunt instrument' and as inefficient because some unchanging
fact which was known at the time the code was written needs to be
'reguessed' with a suitable heuristic at run time. It is also as
invasive because it can only work when it is known if the current
element is the last element.

A general workaround is possible based on special-casing the first
element of the "set of things we're iterating over" because it is always
known if the current element is the first element. But this would still
require support at the language level for it to be sanely applicable to
all kinds of supported loops.

Something Wikipedia calls 'loop with test in the middle' can also be
built in Perl:

----------
my $line;

defined($line = <>) and do {
LOOP: {
print($line);
$line = <> // last;
print("---\n");
redo;
}
};
 
R

Rainer Weikusat

It would be possible for a loop like
for VAR (LIST)
because it appears that LIST is evaluated once when the loop starts,
so it knows the list of values to be iterated over.

In general (for (EXPR1; EXPR2; EXPR3) or while (EXPR)), I don't see
how the loop structure could know whether it's on the last iteration
or not, because there might be global side effects or dependencies on
the loop iteration and/or termination.

It doesn't need to 'know' that, all which is necessary is - for each
iteration - execute the ordinary loop body before the condition is
evaluated and the 'again' ('onrepeat' seems a better name to me) block
after evaluating the condition resulted in the descision that another
iteration will be done. Some programming languages support
condition-less loops, eg,

http://www.postgresql.org/docs/8.4/...uctures.html#PLPGSQL-CONTROL-STRUCTURES-LOOPS

Perl loop control statements work with bare blocks, hence, unconditional
loop can be implemented as

{
# some code here
redo;
}

and this enables putting a test for loop termination anywhere in between
which provides the desired semantics. But this works only for loops
with an explicit condition, not for 'loop over the members of the set'
loops and it requires testing the condition once before entering the
block for loops which start with testing the condition. Also, this is
bound to confuse people who expect that loops 'look' like loops and
don't hide in bare blocks and suddenly jump at the without warning.

Something which works exactly like a continue block, only that the code
would be executed immediately after evaluating the condition instead of
befor doing so would be a much nicer alternative, not the least because
it would mean a description of the intent of the code could be provided
to the compiler which would then utilize a suitable way of achieving
that instead of one the the numerous ways to 'interfere creatively' with
the control-flow of an ordinary loop which were presented in this
thread.
 
T

Ted Zlatanov

h> So I've got a for loop. It's doing some printing, and at the end of
h> what it prints, it prints a separator line likeh> Is there an easy way that I can tell my loop "don't print the separator
h> after the last iteration"?
BM> If you're going to build an array anyway, you might as well just use
BM> join. (If you want more general 'joining', see List::Util::reduce.)

join() is sometimes less efficient because it builds a temporary string
just for printing it out. But my approach often requires a temporary
array, which is sometimes less expensive, sometimes more than join()...
It depends on the task, really.

A big advantage of using a temporary array is that it can be augmented
during the loop. I've build simple crawlers (web and data structure)
that way, pushing things at both ends of @list. A fire-and-forget
folding (reducing) function is tempting but often not the right way for
these situations.

Regarding List::Util::reduce, it also does not tell you that you're at
the last iteration. That really is a useful piece of knowledge in many
cases.

Ted
 
T

Ted Zlatanov

g> I miss in this discusion a solution using when/default.
g> Can not be as efficient as using if?

When you have to use older Perls a lot, like I do, you tend to avoid
when/default. It's not bad, just annoying when I have to rewrite the
code and deal with complaints, so I often stick to the oldest common
denominator (OCD) :)

Specifically in this case, the efficiency is about the same and I was
writing an example that can be copy-and-pasted easily, but normally I'd
store the check result instead of recalculating it twice.

Ted
 
T

Ted Zlatanov

On Mon, 3 Mar 2014 18:14:56 +0000 (UTC) (e-mail address removed) (Tim McDaniel) wrote:


TM> Isn't it also experimental and complicated?

I'm not against it, but personally prefer the semantics of Lisp's cond
or C/C++/Java's switch. Recently I've found the `pcase' macro in Emacs
Lisp, which "performs ML-style pattern matching," interesting as well.

I think at least half of the fun in programming is trying new toys :)

Ted
 
R

Rainer Weikusat

Ben Morrow said:
Quoth (e-mail address removed):

Smartmatch, given and when have recently (5.18) been retroactively
marked experimental. 5.18 will warn if it sees them, unless you've
disabled the warning: see perl518delta. The semantics are rather badly
designed, and are likely to change, if the features don't simply do away
altogether.

The actual text says

Smart match, added in v5.10.0 and significantly revised in
v5.10.1, has been a regular point of complaint. Although there
are a number of ways in which it is useful, it has also proven
problematic and confusing for both users and implementors of
Perl. There have been a number of proposals on how to best
address the problem. It is clear that smartmatch is almost
certainly either going to change or go away in the
future. Relying on its current behavior is not recommended.

Warnings will now be issued when the parser sees ~~ , given , or
when.

[...]

Consider, though, replacing the use of these features, as they
may change behavior again before becoming stable.

http://perldoc.perl.org/5.18.0/perldelta.html#The-smartmatch-family-of-features-are-now-experimental

AIUI, this does not state that the idea to introduce a real switch/ case
statement into Perl was 'a rather bad one'[*] but that the DWIM-approach
behind 'smart matching' which has been marked as experimental turned out
to be "Damn Warren's Infernal Machine" for any who wasn't Warren, as
this kind of things are wont to go, IOW, that a lot of people 'meant'
smart matching to do something completely different than what its
author(s) happened to consider "the only sensible proposition" for each
given case.

Considering this, I think that it is likely that Perl will retain the
given/ when/ default part, although with different and possibly entirely
'unsmart' matching semantics, eg, use equality instead, and probably,
with some way to request for 'set matches', ie, consider a condition as
true when the given value is a member of somehow specified list. OTOH,
the ~~-operator might will disappear entirely if it turns out to be
impossible to agree upon sensible semantics (and I certainly wouldn't
mind that as I found 'smart matching' rather dumb on several occasions).

[*] This doesn't necessarily mean that the focus isn't really about the
sentiments of people who go ballistic once the encounter something which
isn't an if-elsif-elsif-else cascade, just that the text doesn't seem to
state this to me.
 
K

Kaz Kylheku

AIUI, this does not state that the idea to introduce a real switch/ case
statement into Perl was 'a rather bad one'[*] but that the DWIM-approach
behind 'smart matching' which has been marked as experimental turned out
to be "Damn Warren's Infernal Machine" for any who wasn't Warren, as

Smart feature sometimes turn out not-so-smart.

A few days ago I took out the "smart quote" out of TXR Lisp.

It breaks backward compatibility, but damn backward compatibility.

This was an experiment in using a single quote character in the read
syntax for both regular quotes and quasiquotes.

So '(a b c) is a quote, but '(a b ,c) was a quasiquote.
What prompted me to experiment in that direction was that the backquote
character was already taken for quasiliterals.

This almost worked fine. Under the hood, the parser would walk the template,
and decide "do I generate the quote operator for this, or the qquote macro?"
There were some hacks like treating ,' unconditionally as a spliced quote.

In the end, the deal-breaker was that the print syntax hid the difference
between the two operators, mapping them back to the apostrophe. Combined with
the several more hacks I had to put in, it made for a bad recipe: you have an
object which prints out as ''(a '(,',a)) or whatever and don't know what exact
structure the quotes stand for; and, more importantly, is it the same structure
that would result if you parsed that notation as a string?

So, the right thing is to perform a hack-ectomy. The backquote operator is
now the caret sign, and quote is just quote.

If a feature is bad, throw it out, ASAP. Don't let another day go by with more
programs depending on it.

Don't be like Dennis Ritchie: oh we can't fix the precedence of the bitwise &
operator now that we have &&, because there is a whopping 600 kilobytes of
existing C code already.
 
R

Rainer Weikusat

Ben Morrow said:
Quoth Rainer Weikusat said:
The actual text says

Smart match, added in v5.10.0 and significantly revised in
v5.10.1, has been a regular point of complaint.
[...]
It is clear that smartmatch is almost
certainly either going to change or go away in the
future. Relying on its current behavior is not recommended.
[...]
http://perldoc.perl.org/5.18.0/perldelta.html#The-smartmatch-family-of-features-are-now-experimental

AIUI, this does not state that the idea to introduce a real switch/ case
statement into Perl was 'a rather bad one'

You are using quote marks. Who are you quoting?

If in doubt, an invisible 'Greg KH' who has no problems with claiming to
be "The Greatest Programmer On Earth" and considering switch-statements
so incomprehensible that they have to be replaced at the same time and
any other dedicated member of the software section of the "There's
nothing worthwhile of being stated which can't be expressed clearly by
a string of differently accentuated 'fucks'"-tribe.

[...]
Well, in principle that's all that smartmatch is supposed to be doing:
equality or regex match or set match, as appropriate. In practice this
falls down at the first hurdle: Perl 5 has *two* equality operators, and
it matters which you use. (It also doesn't have a well-defined
reification of 'a class', making is-a tests equally problematic.)

I was writing about 'equality' and possibly, some notion of set
matches. I didn't include regexes because these can already be matched
against $_ without writing the '$_ =~'-part every time. I was also being
intentionally vague on 'set matches' because this was supposed to
include the possibility of something with a much more limited scope than
the 14 different set matches documented for perl 5.10.1 smart matching.

I'll address the 'equality' issue below.
If you can define sensible and useful semantics for 'when', then ~~ can
be defined to do the same thing.

That would hardly be worthwhile.

[...]
OTOH, given/when without some sort of smart matching just isn't that
useful.

Trying to be so ueber-smart that the final result is nothing because the
problem invariably diffuses into a cloud of nebolous opinions which all
have something going for and against them it isn't particular smart in
the end. "Do something less ambitious for now and leave the complicated
stuff for the future" is IMHO a better approach. Eg, a C
switch-statement requires all cases to be integer constants. That's
already useful when dealing with functions returning some discrete
values and possibly, a default case, eg, fork or <=> or anything which
returns 'magic numbers' to indicate different things. 'Equality' than
becomes == and if this doesn't do what is desired, another construct has
to be used. Going one step further, one could allow 'string literals'
(including interpolation) as well. 'Equality' than mean 'use == if it is
an integer constant and eq otherwise'. Additionally dealing with literal
regexes shouldn't be a big problem.

This would cover almost everything I've been using given/when for so
far and would still go beyond what is provided by the most 'current'
version of Java. I've actually used functions a la

sub eql($)
{
return $_ == $_[0];
}

or

sub equal($)
{
return $_ eq $_[0];
}

in the past in 'switch statements implemented as for loop' to avoid
repeating the '$_' (of course, I want to use that, that's why I wrote
for (something) {} in the first place) and the same comparison operator
every time.

Another simplistic addition would be to allow 'range operator
statements' in order to match a sequence of integers or literal strings.
(given doesn't even alias, making it's most obvious use, as an
equivalent to VB's 'with', impossible.)

AFAIK, with-statemens have existed in Pascal since "the dawn of times"
(relative to the age of VB, that is) ...
 
R

Rainer Weikusat

Ben Morrow said:
Do you really think so? I don't. Given a choice between (say)

given ($pid) {
when (undef) {
die "fork failed";
}
when (0) {
child();
}
default {
parent();
}
}

and

if (!defined $pid) {
die "fork failed";
}
elsif ($pid == 0) {
child();
}
else {
parent();
}

I think the if/elsif wins on both concision and clarity.

Practically, this will often be

given (fork()) {
when (undef) {
}

when (0) {
}
}

ie, the return value doesn't have to be stored in a variable if it isn't
ever going to be used except for determining the result of the
fork. Even if that becomes

given ($pid = fork() {
when (undef) {
}

when (0) {
}
}

it is still immediately evident that there's one operation which
produces a result and different code blocks dealing with several,
possible outcomes. This may also be the case for an if - elsif - else
cascade but it need not be: Only the last two branches are necessarily
related to each other, any remaining ones could be completely
independent. And there are no keywords chaining subsequent blocks
together because the 'else' is implied in the construct.

[...]
You are assuming here that the condition part of the when (or the RHS of
~~) can only be a literal? That mostly solves the typing problem, though
it would necessarily be restricted to *real* literals: a sub returning a
constant would not do, because constants like that are subject to type
coercion just like any other values. (That is, just because EPERM was
defined as an integer constant originally, that does not mean it will
still be (only) an integer constant by the time we get to compiling the
when statement.)

Judging from performing some tests with Perl constants, this seems to be
a problem which could be solved. The main issue here seem to be 'string
constants' whose values can be interpreted as valid
numbers. Distinguishing these from numerical constants which acquired a
string representation because of the inner workings of Perl would
require something like a set of 'originally, I was a ...' flags.
 
R

Rainer Weikusat

Ben Morrow said:
Yes, it would. Given that perl has already run out of SvFLAGS, and goes
through some nasty contortions to reuse flag bits wherever it's
unambiguous, I don't think adding new bits just for the sake of given/
when is a good trade-off. Nor is extending the size of the flags field:
that would make every SV larger, reducing the number of SVs which fit in
processor cache, and making every program somewhat slower.

Keeping additional meta-information about 'Perl constants' supposed to
be used solely while compiling wouldn't necessarily make anything
'larger' at run time.
Perl's design is that is has polymorphic types and monomorphic
operators. Trying to create operators which are polymorphic on the types
of their operands never ends well; instead, different operators should
be used, which coerce their operands as appropriate.

I don't disagree with statement. What I was writing about was to enable
the Perl compiler to select 'an operator' for a comparison at
compile-time, based on the known type of one of its arguments.That would
be a somewhat more feature-full equivalent to 'multiway conditionals' in
other languages.

And this is certainly perfectly doable.
 
R

Rainer Weikusat

Ben Morrow said:
Given that perl has BEGIN and eval/do/require, any information that
might be needed at some later compile time has to also be preserved at
run time. You're right that this information could be kept only for
constants, and could be kept out-of-band in some way so it's only
accessed when it's needed, but this still means increasing the storage
required for a constant sub.

A 'constant' PVIV or PVNV has two length fields like any other
'PV-thing' and considering that it's string representation won't grow,
one of them isn't really needed. It also has a reference count which
isn't really needed (or, more correctly, most bits of which aren't ever
going to be used). It's very likely that there are more unused bits in
this memory area, malloc metainformation would come to mind here. Not
caching 'type conversion results' in the original object itself on the
grounds that they'll be rarely - if ever - useful for 'typical cases'
might also be a viable option.

This is an engineering problem and it can certainly be solved in the
sense of meeting any more specific technical requirements than "don't do
anything because that would cause an effect".
constant.pm goes to some trouble to minimise this storage, because it
makes a measurable difference to the memory usage of Perl programs;
reversing that for the sake of a questionable syntactic shortcut
doesn't seem like a good idea.

That would be 'for the sake of a standard feature of every high-level
programming language I've used so far which Perl unfortunately
traditionally lacks' (or 'used to lack until 2007 and may again lack "in
the future"').
'Selecting an operator at compile time based on the known type of its
arguments' is just another way of saying 'creating an operator which is
polymorphic on the types of its operands'.

'An operator which is polymorphic on the types of its operands' would be
a multi-method (or 'a generic function' in CLOS terminology) which can
be used at run time and which inspects the types of its actual operands
for dispatching whenever it is invoked. Making the compiler infer an
existing non-polymorphic operator based on the 'static' type of one
operand is something rather different from that.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,093
Messages
2,570,607
Members
47,227
Latest member
bluerose1

Latest Threads

Top