Re: How to distinct the call position of a sub?
by kyle (Abbot) on Nov 24, 2008 at 18:27 UTC
|
I suspect you'd have to use a source filter to get the behavior that you're looking for, and then you run the risk of behavior you're not looking for.
The next best thing would be to document that your iter() won't work if called twice from the same source line. I'd want to reformat the line you have anyway. If you're so inclined, you could allow an optional third parameter in which the programmer has to write a unique name (or gamble on rand) to distinguish calls on the same line.
Ultimately, I'd say, don't do this. It makes more sense for me, the maintenance programmer, to have an "initialization" of the iterator somewhere followed by multiple calls to the iterator to get its results.
Want to mess with the mind of the reader? Make this work:
for( my $x = iter(1,10); $x; $x++ ) {
print "$x\n";
}
(Make iter() return an object that's overloaded with stringification and numification to the "current" value and "++" that proceeds to the next value. You might even be able to do this with only tie.) | [reply] [d/l] [select] |
Re: How to distinct the call position of a sub?
by Old_Gray_Bear (Bishop) on Nov 24, 2008 at 18:31 UTC
|
And caller() doesn't help?
my ($package, $file, $line, $subname, $hasargs, ...) = caller();
I'd think that the combination of $package, $line, and $subname ought to be pretty specific.....
----
I Go Back to Sleep, Now.
OGB
| [reply] [d/l] |
|
|
Old_Gray_Bear,
I would agree with the caveat that it would likely fail to DWIM in many situations. Assuming way too much about how LanX will use it, the following seems to work:
#!/usr/bin/perl
use strict;
use warnings;
{
my %seen;
sub iter {
my ($beg, $end, $by) = @_;
my $key = join '*', caller();
return $seen{$key}->() if $seen{$key};
my $pos = $beg - $by;
$seen{$key} = sub {
$pos += $by;
if ($pos > $end) {
delete $seen{$key};
return;
}
return $pos;
};
return $seen{$key}->();
}
}
while (my $x = iter(1, 10, 1)) {
print "$x\n";
while (my $y = iter(1, 10, 1)) {
print "$x - $y\n";
}
}
Update: In some versions of perl (5.8.8 and 5.10.0 reported in the CB), an infinite loop results if you delete the print "$x\n"; line. It seems that unless there is some executable statement between the two while loops, caller reports them as happening on the same line. While I think this is a bug in perl, it also shows why this is not a good solution. Running perl -MO=Deparse doesn't seem to support why perl believes they are on the same line.
| [reply] [d/l] [select] |
|
|
Seems to be rather fragile... i.e. for me it works with Perl 5.10.0 if you
leave in the print "$x\n"; (without it, I can replicate the
infinite loop), but with Perl 5.8.8, the iterator returns the wrong values
(with and without the print "$x\n"; — no infinite loop here, though):
| [reply] [d/l] [select] |
|
|
Well ... would you publish a syntax extension with the footnote "but never use it in the same line!" ?
And beside this, iterator are just an example for a general problem to know "which call is this".
Think about a position sensitive function you want to call twice within a ternary operator ... "never use it in the same line?"
And even when I publish it with this restriction, I have no means to throw an error-message if used in a wrong way.
So if there is a general clean solution, where else should I try to ask for wisdom? And shouldn't I ask before implementing an imperfect solution? 8 )
| [reply] |
|
|
And beside this, iterator are just an example for a general problem to know "which call is this".
I don't think there's a reliable solution, which is why I think any design based on having this information is flawed. What do you expect to get if you're called from string eval?
| [reply] |
|
|
Re: How to distinct the call position of a sub?
by jeffa (Bishop) on Nov 24, 2008 at 18:49 UTC
|
| [reply] [d/l] [select] |
|
|
| [reply] |
|
|
| [reply] [d/l] [select] |
|
|
Re: How to distinct the call position of a sub?
by GrandFather (Saint) on Nov 24, 2008 at 20:04 UTC
|
Wrong question. The better question is "How should I format the following so it is readable, maintainable, oh, and so caller tells me which iter is being called?". Phrased like that the answer is obvious:
while (my $x = iter (1, 10)) {
while (my $y = iter (1, 10)) {
print "$x$y"
}
}
with the bonus that it is then easy to see that there was a missing ( for the second while expression.
Perl reduces RSI - it saves typing
| [reply] [d/l] |
|
|
C:\test>perl
sub iter{ warn join '|', caller; return int rand 3 }
for(1 .. 10) {
if( iter() == 0 ) {
print 'first';
}
elsif( iter() == 1 ) {
print 'second';
}
else {
print 'third';
}
}
^Z
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
main|-|3 at - line 1.
thirdthirdthirdthirdfirstthirdsecondthirdfirstthird
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
Re: How to distinct the call position of a sub?
by moritz (Cardinal) on Nov 24, 2008 at 22:23 UTC
|
Sorry, but to me that's design smell.
If your iterator cares about how and where it's being used, you basically prohibit all uses that you didn't think of, thus defeating the idea of code reuse.
The usual approach that you consider to be too verbose has the advantages that it
- Does not rely on black magic
- An iterator can be shared between loops (ie used in different places)
- Can be passed around to subroutines, can be serialized
I think that ease of use on the one hand and ease of use on the other hand are not contradictory; one approach would be to use a tied array, so that you can iterate over it with a simple for loop.
Other ideas are to look on CPAN for lazy list implementations. (I've written Perl6::GatherTake as a proof of concept; it goes a bit in that direction, though I'm sure that you'll find much better suited modules).
I also think that if you ask broader, like "how do I write an iterator that's easy to use?" you might get even more clever ideas from the assembled monks. | [reply] [d/l] |
|
|
Moritz, why should a position depending use-case be passed around??? Do you pass around the head of foreach or the flip-flop-operator?
Sorry your completely missing the point, to find a solution for a one-way and one-place iterator+generator.
Evidently if the iterator has to be passed around, you definetely need a initial construction in a generator. CPAN has this already.
If you can't imagine, better think of literals: nobody initializes a variable and use it just once in the next line, when he can just use a literal string...
TIMTOWTDI
| [reply] |
Re: How to distinct the call position of a sub?
by Anonymous Monk on Nov 25, 2008 at 08:35 UTC
|
while (my $x=iter(0, 1,10)) { while (my $y=iter(1, 1,10)) { print "$x$
+y" }}
?? | [reply] [d/l] |
|
|
How should iter() know which value to return? ... for $x or $y?
| [reply] |