every($key, $interval) function
every($key, $interval) function
am 08.01.2008 23:18:35 von Ted Zlatanov
I find this function very useful:
{
my %counters = ();
sub every
{
my $id = shift @_;
my $when = shift @_;
return (0 == ++$counters{$id} % $when);
}
}
I call it like this:
print "debug info: ..."
if every("debug spot", 50);
print "extra info: ..."
if every("extra spot", 100);
So the debug info will be printed on every 50th call to every(). It's
small and convenient, but I have to make up a new ID every time.
I'd like to preserve the functional interface, so no OOP cheating. How
can I avoid the $id parameter, creating an automatically localized
every() call each time? I thought of __FILE__ and __LINE__ but those
are not unique enough, since you could potentially have
if (every(50)||every(33)) {}
on a line... I also thought of every(\&action_sub, 50) but that avoids
the problem sideways (it doesn't create a unique key, but makes the
action the key, which is not the same thing).
Ted
Re: every($key, $interval) function
am 09.01.2008 01:21:47 von rvtol+news
Ted Zlatanov schreef:
> {
> my %counters = ();
> sub every
> {
> my $id = shift @_;
> my $when = shift @_;
>
> return (0 == ++$counters{$id} % $when);
> }
> }
>
> I call it like this:
>
> print "debug info: ..."
> if every("debug spot", 50);
>
> print "extra info: ..."
> if every("extra spot", 100);
>
> So the debug info will be printed on every 50th call to every(). It's
> small and convenient, but I have to make up a new ID every time.
>
> I'd like to preserve the functional interface, so no OOP cheating.
> How can I avoid the $id parameter, creating an automatically localized
> every() call each time? I thought of __FILE__ and __LINE__ but those
> are not unique enough, since you could potentially have
>
> if (every(50)||every(33)) {}
>
> on a line... I also thought of every(\&action_sub, 50) but that
> avoids the problem sideways (it doesn't create a unique key, but
> makes the action the key, which is not the same thing).
#!/usr/bin/perl
use strict;
use warnings;
sub every_factory {
my $d = shift;
my $n;
return sub { !(++$n % $d) }
}
my ($everyX, $everyY) = (every_factory(3), every_factory(5));
for my $i (1..50) {
print "$i:X\n" if $everyX->();
print "$i:Y\n" if $everyY->();
}
--
Affijn, Ruud
"Gewoon is een tijger."
Re: every($key, $interval) function
am 09.01.2008 01:59:00 von Ted Zlatanov
On Wed, 9 Jan 2008 01:21:47 +0100 "Dr.Ruud" wrote:
R> #!/usr/bin/perl
R> use strict;
R> use warnings;
R> sub every_factory {
R> my $d = shift;
R> my $n;
R> return sub { !(++$n % $d) }
R> }
R> my ($everyX, $everyY) = (every_factory(3), every_factory(5));
R> for my $i (1..50) {
R> print "$i:X\n" if $everyX->();
R> print "$i:Y\n" if $everyY->();
R> }
That's neat, creating $n and returning a subroutine that has it in
scope. I don't like it as much as every(50) because it separates the
number of cycles from the usage of the function, so you'd have to look
back up to remember how many cycles $everyX will use.
I was hoping to create something truly unique and use it as the key
automatically, like __FILE__:__LINE__ but allowing more than one per
line. I tried `caller 0' in list context but it is not enough.
__COLUMN__ would be perfect, if it existed (I don't know of such a
thing).
Ted
Re: every($key, $interval) function
am 09.01.2008 02:36:58 von rvtol+news
Ted Zlatanov schreef:
> Dr.Ruud:
>> #!/usr/bin/perl
>> use strict;
>> use warnings;
>
>> sub every_factory {
>> my $d = shift;
>> my $n;
>> return sub { !(++$n % $d) }
>> }
>
>> my ($everyX, $everyY) = (every_factory(3), every_factory(5));
>
>> for my $i (1..50) {
>> print "$i:X\n" if $everyX->();
>> print "$i:Y\n" if $everyY->();
>> }
>
> That's neat, creating $n and returning a subroutine that has it in
> scope.
"closure"
> I don't like it as much as every(50) because it separates the
> number of cycles from the usage of the function, so you'd have to look
> back up to remember how many cycles $everyX will use.
Then just call it $every50.
Or compromise:
#!/usr/bin/perl
use strict;
use warnings;
sub every{
my %counters if 0;
my ($div, @id) = @_;
return !(++$counters{ caller(), $div, @id } % $div);
}
for my $i (1..13) {
print "A:$i\n" if every(3); print "B:$i\n" if every(3,"B");
print "C:$i\n" if every(5);
}
--
Affijn, Ruud
"Gewoon is een tijger."
Re: every($key, $interval) function
am 09.01.2008 03:18:54 von Ben Morrow
Quoth Ted Zlatanov :
> I find this function very useful:
>
> {
> my %counters = ();
> sub every
> {
> my $id = shift @_;
> my $when = shift @_;
>
> return (0 == ++$counters{$id} % $when);
> }
> }
>
> I call it like this:
>
> print "debug info: ..."
> if every("debug spot", 50);
>
> print "extra info: ..."
> if every("extra spot", 100);
>
> So the debug info will be printed on every 50th call to every(). It's
> small and convenient, but I have to make up a new ID every time.
>
> I'd like to preserve the functional interface, so no OOP cheating. How
> can I avoid the $id parameter, creating an automatically localized
> every() call each time? I thought of __FILE__ and __LINE__ but those
> are not unique enough, since you could potentially have
>
> if (every(50)||every(33)) {}
>
> on a line... I also thought of every(\&action_sub, 50) but that avoids
> the problem sideways (it doesn't create a unique key, but makes the
> action the key, which is not the same thing).
The obvious answer is to call it like
every(my $dummy, 50)
and key your hash on refaddr \$_[0], or, better, make it a fieldhash
(Hash::Util::Fieldhash, new with 5.10). An alternative would be to write
a tiny bit of XS that grabs the return op number from the top entry in
the call stack: basically, this would key on the place in the source
where every was called.
Ben
Re: every($key, $interval) function
am 09.01.2008 04:30:35 von rvtol+news
Ben Morrow schreef:
> Ted Zlatanov:
>> I find this function very useful:
>>
>> {
>> my %counters = ();
>> sub every
>> {
>> my $id = shift @_;
>> my $when = shift @_;
>>
>> return (0 == ++$counters{$id} % $when);
>> }
>> }
>>
>> I call it like this:
>>
>> print "debug info: ..."
>> if every("debug spot", 50);
>>
>> print "extra info: ..."
>> if every("extra spot", 100);
>>
>> So the debug info will be printed on every 50th call to every().
>> It's small and convenient, but I have to make up a new ID every time.
>>
>> I'd like to preserve the functional interface, so no OOP cheating.
>> How can I avoid the $id parameter, creating an automatically
>> localized every() call each time? I thought of __FILE__ and
>> __LINE__ but those are not unique enough, since you could
>> potentially have
>>
>> if (every(50)||every(33)) {}
>>
>> on a line... I also thought of every(\&action_sub, 50) but that
>> avoids the problem sideways (it doesn't create a unique key, but
>> makes the action the key, which is not the same thing).
>
> The obvious answer is to call it like
> every(my $dummy, 50)
> and key your hash on refaddr \$_[0]
> [...]
#!/usr/bin/perl
use strict;
use warnings;
sub every{
my %counters if 0;
(++$counters{ \$_[1] } % $_[0]) or $_[1] and print $_[1], "\n";
}
for my $i (1..13) {
every(7, my $ev0);
every(3, my $ev1 = "A:$i");
every(3, my $ev2 = "B:$i");
every(5, my $ev3 = "C:$i");
}
--
Affijn, Ruud
"Gewoon is een tijger."
Re: every($key, $interval) function
am 09.01.2008 06:08:11 von Uri Guttman
>>>>> "R" == Ruud writes:
R> sub every{
R> my %counters if 0;
don't use that broken idiom. it will fail under 5.10 since it supports
state variables.
R> my ($div, @id) = @_;
R> return !(++$counters{ caller(), $div, @id } % $div);
just for education of others, that is the old pseudo multilevel hash
from perl4 days. it is rare to see a good use for it these days.
uri
--
Uri Guttman ------ uri@stemsystems.com -------- http://www.sysarch.com --
----- Perl Architecture, Development, Training, Support, Code Review ------
----------- Search or Offer Perl Jobs ----- http://jobs.perl.org ---------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
Re: every($key, $interval) function
am 09.01.2008 12:12:21 von rvtol+news
Uri Guttman schreef:
> Ruud:
>> sub every{
>> my %counters if 0;
>
> don't use that broken idiom.
Here it hasn't broken yet.
> it will fail under 5.10 since it supports
> state variables.
I'll try not to forget to add a comment like "statical, of course use a
state variable in Perl >=5.10" next time.
Or maybe better, refer to `perldoc -q static`.
>> my ($div, @id) = @_;
>> return !(++$counters{ caller(), $div, @id } % $div);
>
> just for education of others, that is the old pseudo multilevel hash
> from perl4 days. it is rare to see a good use for it these days.
See also "$;" in `perldoc perlvar`.
--
Affijn, Ruud
"Gewoon is een tijger."
Re: every($key, $interval) function
am 09.01.2008 14:36:21 von Ted Zlatanov
On Wed, 9 Jan 2008 02:36:58 +0100 "Dr.Ruud" wrote:
R> Ted Zlatanov schreef:
>> Dr.Ruud:
>>> #!/usr/bin/perl
>>> use strict;
>>> use warnings;
>>
>>> sub every_factory {
>>> my $d = shift;
>>> my $n;
>>> return sub { !(++$n % $d) }
>>> }
>>
>>> my ($everyX, $everyY) = (every_factory(3), every_factory(5));
>>
>>> for my $i (1..50) {
>>> print "$i:X\n" if $everyX->();
>>> print "$i:Y\n" if $everyY->();
>>> }
>> I don't like it as much as every(50) because it separates the
>> number of cycles from the usage of the function, so you'd have to look
>> back up to remember how many cycles $everyX will use.
R> Then just call it $every50.
The point is I'm forced to think of a variable and how to use it, plus I
need to initialize it, instead of saying every(50). I think that
complicates life for the user because the code is not simple enough
(it's neat, as I already mentioned, but just not simple).
R> Or compromise:
R> #!/usr/bin/perl
R> use strict;
R> use warnings;
R> sub every{
R> my %counters if 0;
R> my ($div, @id) = @_;
R> return !(++$counters{ caller(), $div, @id } % $div);
R> }
R> for my $i (1..13) {
R> print "A:$i\n" if every(3); print "B:$i\n" if every(3,"B");
R> print "C:$i\n" if every(5);
R> }
R> Ben Morrow schreef:
>> The obvious answer is to call it like
>> every(my $dummy, 50)
>> and key your hash on refaddr \$_[0]
>> [...]
R> #!/usr/bin/perl
R> use strict;
R> use warnings;
R> sub every{
R> my %counters if 0;
R> (++$counters{ \$_[1] } % $_[0]) or $_[1] and print $_[1], "\n";
R> }
R> for my $i (1..13) {
R> every(7, my $ev0);
R> every(3, my $ev1 = "A:$i");
R> every(3, my $ev2 = "B:$i");
R> every(5, my $ev3 = "C:$i");
R> }
Both of those are close but still requires thinking because the code is
not smart enough (as I mentioned, a __COLUMN__ value similar to __LINE__
and __FILE__ would do the right thing, or see below for opcode fun).
The goal is to do
warn "five or six" if (every(5) || every(6));
without external modules, if possible. I don't want to think about the
every() function whenever I use it: the bugs that would come from the
example above if it was badly implemented are just too nasty.
So far it seems like Ben's idea is the best way to do what I mean with
core Perl, but it still bothers me that I can't avoid creating state
*outside* the function call despite what caller() offers. Please
understand, I like all the solutions and they are clever, but they don't
get to the simplicity of the example above.
On Wed, 9 Jan 2008 02:18:54 +0000 Ben Morrow wrote:
BM> An alternative would be to write a tiny bit of XS that grabs the
BM> return op number from the top entry in the call stack: basically,
BM> this would key on the place in the source where every was called.
That's exactly what I'd like, without the XS :)
Could I do it with Inline::C? How about a module that extends caller()
to do this (the DB override to caller() doesn't seem sufficient)? Maybe
one of the core debugging/profiling modules already provides this
information? I couldn't find it.
Thanks to everyone for the suggestions!
Ted
Re: every($key, $interval) function
am 09.01.2008 17:37:07 von Uri Guttman
>>>>> "R" == Ruud writes:
R> Uri Guttman schreef:
>> Ruud:
>>> sub every{
>>> my %counters if 0;
>>
>> don't use that broken idiom.
R> Here it hasn't broken yet.
it is broken now as it isn't documented. it is a bug in perl that lets
that code 'work'. using it is a poor idea and even worse showing it to
others on usenet. it is trivial to get such a thing with a block surrounding
the sub and putting the my variable out there. there is no reason to use
the my/if code and plenty of reasons to not use it.
uri
--
Uri Guttman ------ uri@stemsystems.com -------- http://www.sysarch.com --
----- Perl Architecture, Development, Training, Support, Code Review ------
----------- Search or Offer Perl Jobs ----- http://jobs.perl.org ---------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
Re: every($key, $interval) function
am 09.01.2008 20:03:43 von Big and Blue
Uri Guttman wrote:
> R> sub every{
> R> my %counters if 0;
>
> don't use that broken idiom. it will fail under 5.10 since it supports
> state variables.
"our %counters" works.
--
Just because I've written it doesn't mean that
either you or I have to believe it.
Re: every($key, $interval) function
am 09.01.2008 20:18:20 von Uri Guttman
>>>>> "BaB" == Big and Blue writes:
BaB> Uri Guttman wrote:
R> sub every{
R> my %counters if 0;
>> don't use that broken idiom. it will fail under 5.10 since it
>> supports
>> state variables.
BaB> "our %counters" works.
and that declares and uses a global which can be modified by outside
code. not a better solution.
uri
--
Uri Guttman ------ uri@stemsystems.com -------- http://www.sysarch.com --
----- Perl Architecture, Development, Training, Support, Code Review ------
----------- Search or Offer Perl Jobs ----- http://jobs.perl.org ---------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
Re: every($key, $interval) function
am 10.01.2008 00:36:30 von Big and Blue
Uri Guttman wrote:
> BaB> "our %counters" works.
>
> and that declares and uses a global which can be modified by outside
> code. not a better solution.
I'd thought that to - so I tested it by trying to access %counters at
the end of the script:
==========
#!/usr/bin/perl
use strict;
use warnings;
sub every{
# my %counters if 0;
our %counters;
(++$counters{ \$_[1] } % $_[0]) or $_[1] and print $_[1], "\n";
}
for my $i (1..13) {
every(7, my $ev0);
every(3, my $ev1 = "A:$i");
every(3, my $ev2 = "B:$i");
every(5, my $ev3 = "C:$i");
}
print keys %counters, "\n";
==========
The result?
[mysys]: /usr/bin/perl -wc x.pl
Variable "%counters" is not imported at x.pl line 18.
Global symbol "%counters" requires explicit package name at x.pl line 18.
x.pl had compilation errors.
So it actually seems to work (removing line 18 lets the script run, and it
produces the same results as the "my %counters" version).
--
Just because I've written it doesn't mean that
either you or I have to believe it.
Re: every($key, $interval) function
am 10.01.2008 01:04:31 von Uri Guttman
>>>>> "BaB" == Big and Blue writes:
BaB> Uri Guttman wrote:
BaB> "our %counters" works.
>> and that declares and uses a global which can be modified by outside
>> code. not a better solution.
BaB> I'd thought that to - so I tested it by trying to access %counters
BaB> at the end of the script:
BaB> sub every{
BaB> # my %counters if 0;
BaB> our %counters;
BaB> (++$counters{ \$_[1] } % $_[0]) or $_[1] and print $_[1], "\n";
BaB> }
BaB> for my $i (1..13) {
BaB> every(7, my $ev0);
BaB> every(3, my $ev1 = "A:$i");
BaB> every(3, my $ev2 = "B:$i");
BaB> every(5, my $ev3 = "C:$i");
BaB> }
BaB> print keys %counters, "\n";
BaB> [mysys]: /usr/bin/perl -wc x.pl
BaB> Variable "%counters" is not imported at x.pl line 18.
BaB> Global symbol "%counters" requires explicit package name at x.pl line 18.
BaB> x.pl had compilation errors.
try it with %main::counters.
you don't understand how our works. it declares a lexically scoped name
(really an alias) to a global in the current namespace. so with strict
you need to fully qualify the name to access it. but it is still a
global and accessible from anywhere.
BaB> So it actually seems to work (removing line 18 lets the script run,
BaB> and it produces the same results as the "my %counters" version).
no, as i said it still is a global and doesn't hide the counter hash at
all.
uri
--
Uri Guttman ------ uri@stemsystems.com -------- http://www.sysarch.com --
----- Perl Architecture, Development, Training, Support, Code Review ------
----------- Search or Offer Perl Jobs ----- http://jobs.perl.org ---------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
Re: every($key, $interval) function
am 10.01.2008 01:19:06 von rvtol+news
Ted Zlatanov schreef:
> The goal is to do
>
> warn "five or six" if (every(5) || every(6));
Use (any variant of) my second approach:
#!/usr/bin/perl
use strict;
use warnings;
sub every{
my %counters if 0;
my ($div, @id) = @_;
return !(++$counters{ caller(), $div, @id } % $div);
}
for my $i (1..32) {
print "five or six:\t$i\n" if every(5) | every(6);
}
__END__
five or six: 5
five or six: 6
five or six: 10
five or six: 12
five or six: 15
five or six: 18
five or six: 20
five or six: 24
five or six: 25
five or six: 30
You just can't use the shortcutting "||", because the rightside every()
would not be called when the leftside every() already is true. (So you
had an xy-problem.)
--
Affijn, Ruud
"Gewoon is een tijger."
Re: every($key, $interval) function
am 10.01.2008 14:05:01 von Ted Zlatanov
On Thu, 10 Jan 2008 01:19:06 +0100 "Dr.Ruud" wrote:
R> Ted Zlatanov schreef:
>> The goal is to do
>>
>> warn "five or six" if (every(5) || every(6));
R> Use (any variant of) my second approach:
R> #!/usr/bin/perl
R> use strict;
R> use warnings;
R> sub every{
R> my %counters if 0;
R> my ($div, @id) = @_;
R> return !(++$counters{ caller(), $div, @id } % $div);
R> }
R> for my $i (1..32) {
R> print "five or six:\t$i\n" if every(5) | every(6);
R> }
R> __END__
R> You just can't use the shortcutting "||", because the rightside every()
R> would not be called when the leftside every() already is true. (So you
R> had an xy-problem.)
OK, this will work for my purposes just fine, but it has one possible
bug (unlikely, granted, but it could happen in a one-liner):
for my $i (1..32) {
print "four once:\t$i\n" if every(4); print "four twice:\t$i\n" if every(4);
}
four twice: 2
four twice: 4
four twice: 6
four twice: 8
four twice: 10
four twice: 12
four twice: 14
four twice: 16
four twice: 18
four twice: 20
four twice: 22
four twice: 24
four twice: 26
four twice: 28
four twice: 30
four twice: 32
As a challenge, can anyone think of a way to fix that without using
extra parameters in @id, keeping the calling semantics just as they are
above, using only core modules (as of 5.10)? I couldn't find a way. My
original problem is solved, this is just for fun.
Ted
Re: every($key, $interval) function
am 10.01.2008 14:43:52 von rvtol+news
Ted Zlatanov schreef:
> it has one possible
> bug (unlikely, granted, but it could happen in a one-liner):
>
> for my $i (1..32) {
> print "four once:\t$i\n" if every(4); print "four twice:\t$i\n"
> if every(4); }
Yes, that's exactly why I added @id. Did you try 4.1 and 4.2 already? :)
--
Affijn, Ruud
"Gewoon is een tijger."
Re: every($key, $interval) function
am 10.01.2008 14:56:58 von rvtol+news
Ted Zlatanov schreef:
> As a challenge, can anyone think of a way to fix that without using
> extra parameters in @id, keeping the calling semantics just as they
> are above
With a change in calling semantics:
#!/usr/bin/perl
use strict;
use warnings;
sub every{ my %n if 0; !(++$n{ caller(), $_[0] } % ${$_[0]}) }
for my $i (1..32) {
print "four:\t$i\n" if every(\4); print "four again:\t$i\n" if
every(\4);
}
--
Affijn, Ruud
"Gewoon is een tijger."
Re: every($key, $interval) function
am 10.01.2008 16:46:27 von Ted Zlatanov
On Thu, 10 Jan 2008 14:56:58 +0100 "Dr.Ruud" wrote:
R> Ted Zlatanov schreef:
>> As a challenge, can anyone think of a way to fix that without using
>> extra parameters in @id, keeping the calling semantics just as they
>> are above
R> With a change in calling semantics:
R> #!/usr/bin/perl
R> use strict;
R> use warnings;
R> sub every{ my %n if 0; !(++$n{ caller(), $_[0] } % ${$_[0]}) }
R> for my $i (1..32) {
R> print "four:\t$i\n" if every(\4); print "four again:\t$i\n" if
R> every(\4);
R> }
That's *nice*. I didn't know Perl would optimize \4 to be a unique
reference per execution, yet distinguish between two of them named
separately:
for my $i (1..32)
{
# prints two distinct SCALAR refs that are the same each time
print \4, \4,"\n";
}
That's wonderfully consistent.
R> Did you try 4.1 and 4.2 already? :)
That's also a neat way around it, but I may as well use extra calling
arguments if I'm going to do that.
Thanks for the great ideas.
Ted
Re: every($key, $interval) function
am 23.01.2008 20:15:27 von Ted Zlatanov
Just a followup: every() has been very useful for progress counters,
e.g.:
sub print_update
{
print '.' if every(50);
print "\n" if every(500);
}
Plus I've used it for other things, like doing a DB commit every N
items, etc.
Here's the final version I'm using, from Dr. Ruud's suggestions:
{
my %counters;
sub every
{
my ($div, @id) = @_;
return !(++$counters{ caller(), $div, @id } % $div);
}
}
Ted