Home > Archive > PERL Beginners > August 2007 > subroutine references
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
subroutine references
|
|
|
| I'm working on yet another exercise from Intermediate Perl. I've been
given a script that searches for files that fall between a two
timestamps. For the exercise, I am supposed to write the
gather_mtime_between subroutine that will return two references to two
subroutines. One will use File::Find that will find the files that
were modified between the two timestamps. The other function should
return a list of the found items.
use strict;
use File::Find;
use Time::Local;
sub gather_mtime_between {
return (
sub {
foreach my $filename(my @file_list){
my $timestamp = (stat $filename)[9];
if (my $start <= $timestamp <= my $stop){
push @found_items, $filename;
}
}
},
sub { return @found_items };
)
}
my $target_dow = 1; # Sunday is 0, Monday is 1, ...
my @starting_directories = (".");
my $seconds_per_day = 24 * 60 * 60;
my($sec, $min, $hour, $day, $mon, $yr, $dow) = localtime;
my $start = timelocal(0, 0, 0, $day, $mon, $yr); # midnight today
while ($dow != $target_dow) {
# Back up one day
$start -= $seconds_per_day; # hope no DST! :-)
if (--$dow < 0) {
$dow += 7;
}
}
my $stop = $start + $seconds_per_day;
my($gather, $yield) = gather_mtime_between($start, $stop);
find($gather, @starting_directories);
my @files = $yield->();
for my $file (@files) {
my $mtime = (stat $file)[9]; # mtime via slice
my $when = localtime $mtime;
print "$when: $file\n";
}
I have 3 questions:
1) The $start and $stop variables in my subroutine are the $start and
$stop variables in the script at large. However, I had to label these
with 'my' because I was getting error messages. Will this cause
trouble?
2) How do I get the array @starting_directories into my first
anonymous subroutine? I need @starting_directories to be in the same
place as @file_list.
3) When I run the script I get these error messages:
syntax error at last_mod.pl line 16, near "$timestamp <="
syntax error at last_mod.pl line 20, near "}"
I can't figure out what is causing these error messages. The various
braces, etc. seem to match up.
| |
| Tom Phoenix 2007-08-25, 7:00 pm |
| On 8/25/07, Chris <cspears2002@yahoo.com> wrote:
> if (my $start <= $timestamp <= my $stop){
Until Perl 6, you have to break down chain comparisons like this into
separate comparisons, usually joined with 'and':
if ($start <= $timestamp and $timestamp <= $stop) { ... }
But the real problem is the my() function, which should rarely if ever
appear within a conditional expression. Use my() to declare lexical
variables at the start of their scope. The scope of a lexical variable
is the part of the program in which these names can be used for these
variables. The scope continues until the end of the smallest enclosing
block or file.
In this case, you need to declare your variables outside the scope of
the subroutine, since you don't want them to be available only within
the sub. Ideally, they should be declared in the smallest possible
scope that needs them. I think you want them to be declared near the
start of gather_mtime_between, using the subroutine parameters in the
@_ variable:
my($start, $stop) = @_; # sub parameter list
That is to say, each invocation of gather_mtime_between would be given
its own $start and $stop values. It can then return closures, which
are subroutines which use those persistant variables from a larger
scope.
Hope this helps!
--Tom Phoenix
Stonehenge Perl Training
| |
| Mr. Shawn H. Corey 2007-08-25, 7:00 pm |
| Chris wrote:
> I'm working on yet another exercise from Intermediate Perl. I've been
> given a script that searches for files that fall between a two
> timestamps. For the exercise, I am supposed to write the
> gather_mtime_between subroutine that will return two references to two
> subroutines. One will use File::Find that will find the files that
> were modified between the two timestamps. The other function should
> return a list of the found items.
>
Why do people who write these books have exercises of little practical value?
When you say return two sub references you do mean two anonymous subs, not just references to two conventional subs?
> use strict;
>
> use File::Find;
> use Time::Local;
>
> sub gather_mtime_between {
my @found_items = ();
> return (
> sub {
my $start = shift @_;
my $stop = shift @_;
my @starting_directories = @_;
my @found_items = ();
> foreach my $filename(my @file_list){
> my $timestamp = (stat $filename)[9];
> if (my $start <= $timestamp <= my $stop){
if( $start <= $timestamp && $timestamp <= $stop ){
> push @found_items, $filename;
> }
> }
> },
# Note that this sub does not use anything from File::Find.
> sub { return @found_items };
> )
> }
Incorporating File::Find into them is still up to you. Hint:
find( sub { ... }, @starting_directories );
This exercise creates what is called a closure. But any closure can be replaced by an object. Objects are easier to understand because closure introduce another layer of compile/run to the program. They should be avoided.
--
Just my 0.00000002 million dollars worth,
Shawn
"For the things we have to learn before we can do them, we learn by doing them."
Aristotle
"If you think Terrans are comprehensible, you don't understand them."
Great Fang Talphon
| |
| Mr. Shawn H. Corey 2007-08-25, 7:00 pm |
| Mr. Shawn H. Corey wrote:
> Chris wrote:
>
> Why do people who write these books have exercises of little practical
> value?
>
> When you say return two sub references you do mean two anonymous subs,
> not just references to two conventional subs?
>
>
> my @found_items = ();
>
>
> my $start = shift @_;
> my $stop = shift @_;
> my @starting_directories = @_;
# Oops, don't add this statement. Sorry, about that.
> my @found_items = ();
>
>
> if( $start <= $timestamp && $timestamp <= $stop ){
>
>
> # Note that this sub does not use anything from File::Find.
>
>
> Incorporating File::Find into them is still up to you. Hint:
> find( sub { ... }, @starting_directories );
>
> This exercise creates what is called a closure. But any closure can be
> replaced by an object. Objects are easier to understand because closure
> introduce another layer of compile/run to the program. They should be
> avoided.
>
>
--
Just my 0.00000002 million dollars worth,
Shawn
"For the things we have to learn before we can do them, we learn by doing them."
Aristotle
"If you think Terrans are comprehensible, you don't understand them."
Great Fang Talphon
| |
| Dr.Ruud 2007-08-25, 7:00 pm |
| Shawn schreef:
> Chris:
>
> Why do people who write these books have exercises of little
> practical value?
An exercise needs to be educational.
> closure introduce another layer of compile/run to the program. They
> should be avoided.
Why would a "closure introduce another layer of compile/run"?
A closure carries an environment, so just call it an object.
--
Affijn, Ruud
"Gewoon is een tijger."
| |
|
| Ok, if anyone is interested, here is my answer:
#!/usr/bin/perl -w
# Testing code for Exercise 6-1. Your task is to write the sub
# gather_mtime_between.
use strict;
use File::Find;
use Time::Local;
my ($start, $stop) = @_;
my @starting_directories = @_;
my @found_items;
sub gather_mtime_between {
return (
sub {
my $timestamp = (stat $_)[9];
if ($start <= $timestamp && $timestamp <= $stop){
push @found_items, $File::Find::name;
} else {
print "No file modified between $start and $stop in
$File::Find::dir\n";
}
},
sub { return @found_items }
)
}
my $target_dow = 5; # Sunday is 0, Monday is 1, ...
@starting_directories = ("/home/io/chris_perl/int_perl/");
my $seconds_per_day = 24 * 60 * 60;
my($sec, $min, $hour, $day, $mon, $yr, $dow) = localtime;
$start = timelocal(0, 0, 0, $day, $mon, $yr); # midnight today
while ($dow != $target_dow) {
# Back up one day
$start -= $seconds_per_day; # hope no DST! :-)
if (--$dow < 0) {
$dow += 7;
}
}
$stop = $start + $seconds_per_day;
my($gather, $yield) = gather_mtime_between($start, $stop);
find($gather, @starting_directories);
my @files = $yield->();
for my $file (@files) {
my $mtime = (stat $file)[9]; # mtime via slice
my $when = localtime $mtime;
print "$when: $file\n";
}
I think my main problem was that I didn't grasp how File::Find works.
After I read the docs on CPAN, the solution came together.
| |
| Dr.Ruud 2007-08-25, 10:03 pm |
| Chris schreef:
> #!/usr/bin/perl -w
Toss the -w, and insert a "use warnings;".
> my ($start, $stop) = @_;
> my @starting_directories = @_;
This doesn't do what I think that you think it does.
> my($sec, $min, $hour, $day, $mon, $yr, $dow) = localtime;
Is the start/top related to today?
What if you want the files of the 2-w -period between 3 w s ago and
1 w ago?
--
Affijn, Ruud
"Gewoon is een tijger."
| |
| Mr. Shawn H. Corey 2007-08-26, 7:01 pm |
| Dr.Ruud wrote:
>
> An exercise needs to be educational.
I have worked in programming for 25 years and during that time I have never use a closure and have never seen one used. I may be harsh in my definitions but to me something that is never used is useless. Teaching people to do useless things is not educa
tional.
>
>
>
> Why would a "closure introduce another layer of compile/run"?
> A closure carries an environment, so just call it an object.
>
Dealing with a closure has two phases. A phases when its created (compiled phase) and one when it's executed (run phase). When you write a closure you have to keep the two phases in mind (and separate).
For example, in the OP's problem there are three parameters: the start time, the stop time, and the staring directories. Since it doesn't make much sense to separate the start ans stop times, I shall treat them as a data set called times. There are four
ways to distribute them:
1. All are parameters to the closure generator. In this case, the closure is not very flexible. What it can tell are if a file has been modified between its runs.
2. The times are closure-generator parameters and the starting directories are closure parameters. This gives a closure that can look at different directories but in the same time interval.
3. The starting directories are closure-generator parameters and the times are closure ones. This gives a closure that looks at the same directories but with different time intervals.
4. All are parameters are closure ones. This gives a closure that is very flexible but is little different from a common sub.
The problem with closures is that you have to write them and their generators at the same time. So you have to keep two different phases in mind, the compile phase and the run phase, while remembering what parameters belong to which phase.
Objects can do the same things as closures, which is store and hide data, but don't have this problem of having to keep in mind two phases of the same code.
--
Just my 0.00000002 million dollars worth,
Shawn
"For the things we have to learn before we can do them, we learn by doing them."
Aristotle
"If you think Terrans are comprehensible, you don't understand them."
Great Fang Talphon
| |
| Randal L. Schwartz 2007-08-26, 7:01 pm |
| >>>>> ""Mr" == "Mr Shawn H Corey" <shawnhcorey@magma.ca> writes:
Mr> Objects can do the same things as closures, which is store and hide data,
Mr> but don't have this problem of having to keep in mind two phases of the
Mr> same code.
But objects have fixed code with variable data. Closures can have variable
code with variable data, including user-specified behavior passed in to a
constructor or a mutator. So, yes, objects can do *some* of the things that
closures do, but not all.
Think of closures as "variables that hold behavior". Sure, maybe you've never
needed that in your legendary 25 years in the industry, but I've used it
*frequently* in my 30 years. :)
--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
| |
| Mr. Shawn H. Corey 2007-08-26, 7:01 pm |
| Randal L. Schwartz wrote:
> Think of closures as "variables that hold behavior". Sure, maybe you've never
> needed that in your legendary 25 years in the industry, but I've used it
> *frequently* in my 30 years. :)
>
Why do you include an insult with every thing you post?
BTW, what legends do you have on me? (web links preferred) (and nothing from stonehenge.com, independent sources only). Do they make me out as mean as you?
--
Just my 0.00000002 million dollars worth,
Shawn
"For the things we have to learn before we can do them, we learn by doing them."
Aristotle
"If you think Terrans are comprehensible, you don't understand them."
Great Fang Talphon
| |
| Randal L. Schwartz 2007-08-26, 7:01 pm |
| >>>>> "Shawn" == Shawn H Corey <shawnhcorey@magma.ca> writes:
Shawn> Why do you include an insult with every thing you post?
I don't think I do. I was only making fun of your claim, since you made the
claim. Why did you include "25 years"? It just sets you up for a fall. :)
Shawn> BTW, what legends do you have on me? (web links preferred) (and nothing
Shawn> from stonehenge.com, independent sources only). Do they make me out as
Shawn> mean as you?
Again, I don't think I've been mean. But I will knock the wind out of any
air-filled claim, and you made one here.
--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
| |
| Randal L. Schwartz 2007-08-26, 7:01 pm |
| >>>>> "Shawn" == "Mr Shawn H Corey" <shawnhcorey@magma.ca> writes:
Shawn> Oh, I get it. When I said 25 years, you thought that I meant 25 years
Shawn> with Perl.
No.
Shawn> Sorry, about the confusion.
No confusion.
Shawn> I have programmed in many different languages and have never seen a
Shawn> closure.
Then you've used a different set of languages from what I have.
Shawn> All programming languages are Turing complete, so it is possible to
Shawn> create a closure in any language;
The second part doesn't follow from the first. Perhaps you don't understand
"closure" well enough to be commenting on it.
Shawn> meaning that if they were useful, they would appear more
Shawn> often.
Also an unsubstantiated conclusion. You're just leaping from one thing to the
next here, and getting deeper in pure speculation instead of justified facts.
Shawn> And I have never seen one (except in academia).
Then you've had a pretty narrow bit of experience for being around for "25
years".
Shawn> And OK, you're not mean; you just have a mean sense of humour.
No, I don't think it's humor. It's just calling BS where I see it, like the
message I'm reply to here. People may laugh, but the intent is communication.
--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!
| |
| Mr. Shawn H. Corey 2007-08-26, 7:01 pm |
| Randal L. Schwartz wrote:
>
> Shawn> Why do you include an insult with every thing you post?
>
> I don't think I do. I was only making fun of your claim, since you made the
> claim. Why did you include "25 years"? It just sets you up for a fall. :)
Oh, I get it. When I said 25 years, you thought that I meant 25 years with Perl. Sorry, about the confusion. I have programmed in many different languages and have never seen a closure. All programming languages are Turing complete, so it is possible
to create a closure in any language; meaning that if they were useful, they would appear more often. And I have never seen one (except in academia).
And OK, you're not mean; you just have a mean sense of humour.
--
Just my 0.00000002 million dollars worth,
the "legendary" Shawn
"For the things we have to learn before we can do them, we learn by doing them."
Aristotle
"If you think Terrans are comprehensible, you don't understand them."
Great Fang Talphon
| |
| Rob Dixon 2007-08-26, 7:01 pm |
| Mr. Shawn H. Corey wrote:
> Dr.Ruud wrote:
>
> I have worked in programming for 25 years and during that time I have
> never use a closure and have never seen one used. I may be harsh in my
> definitions but to me something that is never used is useless. Teaching
> people to do useless things is not educational.
Sorry Shawn, but if you're going to claim that anything you haven't seen in
your illustrious 25-year career is useless than the rest of your post isn't
worth reading.
Rob
| |
| Brian D Foy 2007-08-26, 10:00 pm |
| In article <46D07B8C.3070604@magma.ca>, Mr. Shawn H. Corey
<shawnhcorey@magma.ca> wrote:
> Chris wrote:
[color=darkred]
> Why do people who write these books have exercises of little practical value?
Exercises are a tough thing. The trick is to:
* find something that demonstrates the feature being discussed
* doesn't need features you haven't shown yet
* isn't dled with a unfamiliar problem domain
* can be completed with what the student likely has already installed
* is limited enough that it can be completed in a reasonable time
* works for the entire audience
Anything with "practical value" is likely to not actually be very
practical to someone else. In this case, sy mins may think the
exercise is very practical, but bio-informaticists may not.
However, practicality is the wrong way to learn. We don't intend to
teach anyone how to do a specific task. We're teaching the tools people
can use to solve all sorts of different tasks. Learn to use the tool
and you can do a lot. Learn a task, and that's all you can do.
Please feel free to send me suggestions for different exercises for the
second edition, or point me toward the book that you wrote so I can
look at your exercises to see how I might make things more practical.
:)
| |
| Chas Owens 2007-08-27, 4:02 am |
| On 8/26/07, Mr. Shawn H. Corey <shawnhcorey@magma.ca> wrote:
> Dr.Ruud wrote:
>
> I have worked in programming for 25 years and during that time I have never use a
> closure and have never seen one used. I may be harsh in my definitions but to me
> something that is never used is useless. Teaching people to do useless things is
> not educational.
snip
So, you have never used sort, map, grep, File::Find::find, or any of
the other Higher Order functions? They all take a code block or
anonymous function that makes use of closures. Oh, and in my ten
years of coding experience I have only been consciously using them for
the last six (when I moved from mostly ESQL/C and 4GL to mostly Perl).
snip
> Objects can do the same things as closures, which is store and hide data,
> but don't have this problem of having to keep in mind two phases of the same code.
snip
Objects in Perl do not do a very good job of data hiding. In fact,
one of the few ways to actually hide data in is to use closures (see
perldoc perltoot).
You seem to not understand closures or what they are useful for. I
would suggest running out and getting Higher Order Perl. It will make
you a better programmer (even if you are a good programmer now).
| |
| Jenda Krynicky 2007-08-27, 7:24 pm |
| From: Chris <cspears2002@yahoo.com>
> Ok, if anyone is interested, here is my answer:
>
> #!/usr/bin/perl -w
>
> # Testing code for Exercise 6-1. Your task is to write the sub
> # gather_mtime_between.
>
> use strict;
>
> use File::Find;
> use Time::Local;
>
> my ($start, $stop) = @_;
> my @starting_directories = @_;
> my @found_items;
>
> sub gather_mtime_between {
> return (
> sub {
> my $timestamp = (stat $_)[9];
> if ($start <= $timestamp && $timestamp <= $stop){
> push @found_items, $File::Find::name;
> } else {
> print "No file modified between $start and $stop in
> $File::Find::dir\n";
> }
> },
> sub { return @found_items }
> )
> }
If you do it this way there is no point in returning the subroutine
references in the first place. You are using global variables here
(even though you declare them with my).
You can't call the gather_mtime_between() twice and get two unrelated
pairs of functions. You should move the declarations INTO the sub
gather_mtime_between(). And fix the assignment from @_.
The exercise is a bit awkward though.
Jenda
===== Jenda@Krynicky.cz === http://Jenda.Krynicky.cz =====
When it comes to wine, women and song, wizards are allowed
to get drunk and croon as much as they like.
-- Terry Pratchett in Sourcery
| |
| Jenda Krynicky 2007-08-27, 7:24 pm |
| From: "Mr. Shawn H. Corey" <shawnhcorey@magma.ca>
> Dr.Ruud wrote:
>
> I have worked in programming for 25 years and during that time I have
> never use a closure and have never seen one used. I may be harsh in
> my definitions but to me something that is never used is useless.
> Teaching people to do useless things is not educational.
Y'knaw son, I've been hacking trees down all my life with this here
hack and its a good hack, got it from my late father, I did. And I've
never user this what you call chainsaw and have never seen one user.
I may be harsh, y'know, but if you ask me, something that is never
used is useless. So go away with your chainsaw and let me go on with
my hacking. I've got this tree to cut down and I havn't got whole day
to do it.
>
> Dealing with a closure has two phases. A phases when its created
> (compiled phase) and one when it's executed (run phase). When you
> write a closure you have to keep the two phases in mind (and
> separate).
I think I know what you mean, though the choice of words is
unfortunate.
> For example, in the OP's problem there are three parameters: the start
> time, the stop time, and the staring directories. Since it doesn't
> make much sense to separate the start ans stop times, I shall treat
> them as a data set called times. There are four ways to distribute
> them:
>
> 1. All are parameters to the closure generator. In this case, the
> closure is not very flexible. What it can tell are if a file has been
> modified between its runs.
>
> 2. The times are closure-generator parameters and the starting
> directories are closure parameters. This gives a closure that can
> look at different directories but in the same time interval.
>
> 3. The starting directories are closure-generator parameters and the
> times are closure ones. This gives a closure that looks at the same
> directories but with different time intervals.
>
> 4. All are parameters are closure ones. This gives a closure that is
> very flexible but is little different from a common sub.
So you proved that this exercise is not the best case for the usage
of closures. I'd agree on that one. BTW, the 2. and 3. are
(especially the way you explain them) perfect examples of function
currying. Another thing you've never user nor seen used. The Perl5
way of function parameter handling doesn't allow for nice support for
currying though.
> The problem with closures is that you have to write them and their
> generators at the same time. So you have to keep two different phases
> in mind, the compile phase and the run phase, while remembering what
> parameters belong to which phase.
Well, I would not call that a problem. Let me give you a differet
example of a closure.
Imagine you are writing a GUI. What you need to do is to specify what
to do when this button is clicked, that button is clicked, ...
There are basically three ways to do that. (Three and half.)
First, Visual Basic, use a naming convention:
Sub ButtonName_OnClick
SomeObject.Frobnicate
End Sub
Sub OtherButtonName_OnClick
OtherObject.Frobnicate
End Sub
Second, C++ (I believe), use function pointers
hResult ButtonName_OnClick() {
SomeObject.Frobnicate();
}
hResult OtherButtonName_OnClick() {
OtherSomeObject.Frobnicate();
}
....
TheWindow.Buttons('ButtonName).AttachHandler(&ButtonName_OnClick);
TheWindow.Buttons('OtherButtonName).AttachHandler(&OtherButtonName_OnC
lick);
Second and half, C#, similar to C++ except that instead of
&ButtonName_OnClick you have to instantiate the right delegate
object.
In both (all) those cases you have to NAME the functions and you end
up with lots of functions that look the same. Just lovely.
Or you can use closures ... the simple ones without a generator:
$window->addButton(-name => 'ButtonName', -title => 'Sumfin', -
onClick => sub {$someObject->frobnicate()});
# and the $someObject does not have to be global!!!
or if the subroutine is more complex and used more times with
different objects:
sub frobincareObj {
my $obj = shift;
return sub {
do this
do that
$obj->prepare( something);
$obj->frobnicate( some params);
...
}
}
....
$window->addButton(-name => 'ButtonName', -title => 'Sumfin',
-onClick => frobnicateObj($someObject));
$window->addButton(-name => 'OtherButtonName', -title => 'Sumfin 2',
-onClick => frobnicateObj($someObject));
You could of course do something like this with objects. That is if
the "type system" doesn't prevent that. Something like:
class callFrobnicate : thisIsNotAClosure {
frobnicable* the_obj;
callFrobnicate( void *obj) { the_obj = obj }
hResult call() {the_obj->frobnicate()}
}
....
TheWindow.Buttons('ButtonName).AttachHandler(new
callFrobnicate(someObject));
How's that more direct?
> Objects can do the same things as closures, which is store and hide
> data, but don't have this problem of having to keep in mind two phases
> of the same code.
I think you are just not used to the posibility that functions could
return functions.
Jenda
===== Jenda@Krynicky.cz === http://Jenda.Krynicky.cz =====
When it comes to wine, women and song, wizards are allowed
to get drunk and croon as much as they like.
-- Terry Pratchett in Sourcery
|
|
|
|
|