Objects/Structures in Perl

O

O. Olson

Hi,
I am for most part familiar C++/Java programming - but not much with
Perl. I am trying to figure out if I can use C style structures in
Perl - they are not classes per se - but sort of similar.

In the following code - which is part of a larger program - I tried
to group the hours, minutes and seconds of time into a combined data
structure. However once I set the current time ($curr_time) - the next
midnight time ($next_midnight) gets changed. Can anyone guess why??

As I said I am not much into Perl - so I might be doing something
obviously wrong.

-------------------------------------------------------------------
use warnings;
use strict;

# Declaration of a data structure that would hold a single line or
record of the file
# This would ensure that the file would never remain open if the
program exits prematurely

my $time = {
HOUR => my $hour,
MINUTE => my $minute,
SECOND => my $second
};

# Defining two variables of the type time
my $curr_time = $time; #Contains the hour, minute, second
my $next_midnight = $time; #Contains the hour, minute, second

# Setting the Next Midnight Time
($next_midnight->{HOUR}, $next_midnight->{MINUTE}, $next_midnight-
{SECOND}) = (23, 59, 58);
print "Next Midnight Time: "; &printTime($next_midnight); print "\n";

# Setting the current time
($curr_time->{SECOND}, $curr_time->{MINUTE}, $curr_time->{HOUR}) =
localtime();
print "curr_time Time: "; &printTime($curr_time); print "\n";
print "But now, Next Midnight Time: "; &printTime($next_midnight);
print "\n";


# This function would print the time variable passed on to it, without
adding a new line character to it
sub printTime()
{
# Declaring a temporary time variable
my $temp_time = $time;
$temp_time = $_[0];
print "$temp_time->{HOUR}:$temp_time->{MINUTE}:$temp_time-
{SECOND}";
}

------------------------------------------------------------
Console Output:
Next Midnight Time: 23:59:58
curr_time Time: 22:50:12
But now, Next Midnight Time: 22:50:12
---------------------------------------------------------

Why did "Next Midnight Time" change?

Thanks to all who help,
O.O.
 
T

Tad McClellan

O. Olson said:
I am trying to figure out if I can use C style structures in
Perl


perldoc -q struct

How can I make the Perl equivalent of a C structure/C++ class/hash or
array of hashes or arrays?
 
J

Jens Thoms Toerring

O. Olson said:
I am for most part familiar C++/Java programming - but not much with
Perl. I am trying to figure out if I can use C style structures in
Perl - they are not classes per se - but sort of similar.

There's the one problem that Perl is neither C++ (or C) nor Java and
it doesn't have C style structures...
In the following code - which is part of a larger program - I tried
to group the hours, minutes and seconds of time into a combined data
structure. However once I set the current time ($curr_time) - the next
midnight time ($next_midnight) gets changed. Can anyone guess why??
As I said I am not much into Perl - so I might be doing something
obviously wrong.
# Declaration of a data structure that would hold a single line or
record of the file
# This would ensure that the file would never remain open if the
program exits prematurely
my $time = {
HOUR => my $hour,
MINUTE => my $minute,
SECOND => my $second
};
# Defining two variables of the type time
my $curr_time = $time; #Contains the hour, minute, second
my $next_midnight = $time; #Contains the hour, minute, second
# Setting the Next Midnight Time
($next_midnight->{HOUR}, $next_midnight->{MINUTE}, $next_midnight-
print "Next Midnight Time: "; &printTime($next_midnight); print "\n";
# Setting the current time
($curr_time->{SECOND}, $curr_time->{MINUTE}, $curr_time->{HOUR}) =
localtime();
print "curr_time Time: "; &printTime($curr_time); print "\n";
print "But now, Next Midnight Time: "; &printTime($next_midnight);
print "\n";

# This function would print the time variable passed on to it, without
adding a new line character to it
sub printTime()
{
# Declaring a temporary time variable
my $temp_time = $time;
$temp_time = $_[0];
print "$temp_time->{HOUR}:$temp_time->{MINUTE}:$temp_time-
{SECOND}"; }

------------------------------------------------------------
Console Output:
Next Midnight Time: 23:59:58
curr_time Time: 22:50:12
But now, Next Midnight Time: 22:50:12
---------------------------------------------------------
Why did "Next Midnight Time" change?

I can't answer this queston - my question is how you did get that
output at all since (for several reasons) your above code isn't
valid Perl?

I guess you need to free yourself at least a bit of your C++/Java
background and use instead what Perl gives you. While there are
many ways to do it, my first approach would probably be to create a
package for times like the following (with a bit of code at the start
that uses the package and outputs something similar to what you
posted):

------8<-------------------------------------------------------#!/usr/bin/perl
#!/usr/bin/perl

use strict;
use warnings;

my $current_time = new MyTime( );
my $next_midnight = new $current_time( 23, 59, 58 );
print "Next Midnight Time: " . $next_midnight->as_text . "\n" .
"curr_time Time: " . $current_time->as_text . "\n",
"But now, Next Midnight Time: " . $next_midnight->as_text . "\n";


package MyTime;

=pod

=head1 METHODS

=over 4

=item new()

new() can be called with 0 to 3 arguments: if there is none the new
object is initialized to the current (local) time, otherwise the
first argument is taken to be the hour, the second to be the minute
and the third as the second. Missing arguments are assumed to be 0.

=cut

sub new {
my $inv = shift;
my $class = ref( $inv ) || $inv;
die "Too many arguments\n" unless @_ <=3;
my $self;
if ( @_ == 0 ) { # no arguments
my @time = localtime( );
$self = { hour => $time[ 2 ],
minute => $time[ 1 ],
second => $time[ 0 ] };
} else {
$self = { hour => $_[ 0 ],
minute => $_[ 1 ] || 0,
second => $_[ 2 ] || 0 };
}
return bless $self, $class;
}

=pod

=item as_text()

Returns a string of the time the object represents in "hh:mm:ss" format.

=cut

sub as_text {
my $self = shift;
return sprintf "%02d:%02d:%02d",
$self->{ hour }, $self->{ minute }, $self->{ second };
}

1;
------8<-------------------------------------------------------

That way you can create new objects (similar to C++ or Java)
representing times and print them. Adding methods that return
or change the hour, minute or second is simple, e.g. for getting
or setting the hour you would just need e.g.

sub hour {
my $self = shift;
$self->{ hour } = shift if exists $_[ 0 ];
return $self->{ hour };
}

Of course, this MyTime package would need quite a bit of error
checking (what happens when you pass it non-numeric or negative
arguments or hours above 23 or minutes or seconds above 59?)
but I hope it gives you an idea of how you could do it in Perl.

Regards, Jens
 
O

O. Olson

It's probably easier to simply create each hashref the way you want it:

my $curr_time={
HOUR => 1,
MINUTE => 2,
SECOND => 3,

};

my $next_midnight={
HOUR => 4,
MINUTE => 5,
SECOND => 6,

};

I don't think I can do this, because I am going to be using this as a
variable. I think I would try Jens solution below.
Some more unrelated comments:


You have (), which strictly speaking defines printTime as a function
that takes no arguments. You don't see any errors because calling it
like you do above (&printTime($next_midnight)) circumvents prototypes.
You should probably leave out both the () on the sub definition, and the
& on function calls.

This is an interesting thing. I looked at "perlsub" - and though they
mention creating subroutines with prototypes - they do not elaborate
on it. For e.g. I tried:

sub printTime($temp_time)
{
print "$temp_time";
}

Assuming $temp_time is a scalar like an integer. The compiler
complains that $temp_time is not defined. In case I do something
like :

sub printTime(my $temp_time)
{
print "$temp_time";
}

I still get the same error. What am I doing wrong?

Thanks again,
O.O.
 
O

O. Olson

Dear Jens,

I this is exactly what I wanted. I searched again on Google - but I
could not get the method you suggest anywhere on the net. I think I
have understood it overall - but I have a few questions.

I can't answer this queston - my question is how you did get that
output at all since (for several reasons) your above code isn't
valid Perl?

I am running this using ActiveState Perl 5.8.7.815 under Windows XP.
I tried rerunning my exact same code that I posted again - and it does
give the same/similar output to what I have posted above.

My Questions:
package MyTime;

Can this package be declared in a separate file - I tried it - but it
would not work. I put this in a file called "MyTime.pl" and in the
original source file put "use main::MyTime;". This does not work.
sub new {
my $inv = shift;
my $class = ref( $inv ) || $inv;
die "Too many arguments\n" unless @_ <=3;
my $self;
if ( @_ == 0 ) { # no arguments
my @time = localtime( );
$self = { hour => $time[ 2 ],
minute => $time[ 1 ],
second => $time[ 0 ] };
} else {
$self = { hour => $_[ 0 ],
minute => $_[ 1 ] || 0,
second => $_[ 2 ] || 0 };
}
return bless $self, $class;

}

I think I did not understand some of the aspects of this new()
function - though I liked the clever way you created a new hash
everytime a new object of the class is required. Keith above mentioned
that I should do this - but he did not elaborate that way you did it.

1. I think from the documentation that "shift" would decrease the size
of the array @_ by 1. So shouldn't that at the top be @_ <=2;

2. I think I did not understand what the second line does:

my $class = ref( $inv ) || $inv;


3. Is $time sort of an implicit variable. I looked at the "localtime"
function at http://perldoc.perl.org/functions/localtime.html and I
could not find any mention of it.

4. Finally I have no idea about:

return bless $self, $class;

I looked at http://perldoc.perl.org/functions/bless.html regarding
bless - but I did not understand how $class would refer to the current
package.
1;
------8<-------------------------------------------------------

I would also like to know if every file in Perl should end with a 1; -
I did not know this before - but only learnt about it when you used
it.

Regards, Jens

Thanks a lot for your code :) I think that's exactly what I wanted.

vielen Danke,
O.O.
 
A

A. Sinan Unur

Can this package be declared in a separate file - I tried it - but it
would not work. I put this in a file called "MyTime.pl" and in the
original source file put "use main::MyTime;". This does not work.

Of course it can be put in a different file but there are rules that
determine where the resulting file goes.

See the section "Perl Modules" in perldoc perlmod.

Second, if the package is called MyTime then the package is called
MyTime not main::MyTime.

I fact, I would prefer not to put packages in the root namespace but
instead adopt the following convention:

package My::Time;

To use this package, suppose your application is in

C:\opt\mytime

I would then put my custom package in c:\opt\mytime\lib\My\Time.pm

That file would start with the package declaration:

package My::Time;

The application, let's assume the executable is mytime.pl, can then
specify the custom location:

#!/usr/bin/perl

use strict;
use warnings;

use File::Spec::Functions qw( catfile );
use FindBin qw( $Bin );

use lib catfile( $Bin, 'lib' );

use My::Time;

my $time = My::Time->new;

# ...
# ...

__END__

There two schools of thought on being able to call new on the class and
the object. I subscribe to the camp that thinks this is unnecessary.

sub new {
my $class = shift;

die "Too many arguments\n" unless @_ <=3;
my $self;
if ( @_ == 0 ) { # no arguments
my @time = localtime( );
$self = { hour => $time[ 2 ],
minute => $time[ 1 ],
second => $time[ 0 ] };
} else {
$self = { hour => $_[ 0 ],
minute => $_[ 1 ] || 0,
second => $_[ 2 ] || 0 };
}
return bless $self, $class;

}

I think I did not understand some of the aspects of this new()
function - though I liked the clever way you created a new hash
everytime a new object of the class is required. Keith above mentioned
that I should do this - but he did not elaborate that way you did it.

Because you are supposed to read the documentation that comes with Perl.
On Windows, look in the ActiveState Perl program group. Follow the link
for HTML docs. At least, read the table of contents. See the topics on
object oriented programming and read them.
1. I think from the documentation that "shift" would decrease the size
of the array @_ by 1. So shouldn't that at the top be @_ <=2;

Again, if you had read the documentation (e.g. perldoc perlobj) you
would have known that the first argument to a method (not a subroutine)
is the name of the class. Again, you can easily check this:

C:\Home\asu1\Src\test\t> cat t.pl
#!/usr/bin/perl

package My::Test;

use strict;
use warnings;

use Data::Dumper;

sub new {
print Dumper \@_;
}

package main;

use strict;
use warnings;

my $t = My::Test->new( qw(a bunch of arguments) );

__END__


C:\Home\asu1\Src\test\t> t
$VAR1 = [
'My::Test',
'a',
'bunch',
'of',
'arguments'
];

2. I think I did not understand what the second line does:

my $class = ref( $inv ) || $inv;

Enables new to be called on an object rather than just the class so you
can do:

my $x = My::Time->new( qw( blah blah blah ) );

my $y = $x->new;

I really see no good reason to do this. It is too confusing for people
of limited telepathic abilities (such as myself) as it is not clear to
me what a method named 'new' should do when invoked on an object.
3. Is $time sort of an implicit variable. I looked at the "localtime"
function at http://perldoc.perl.org/functions/localtime.html and I
could not find any mention of it.

Ahem, I cannot see any variable called $time in Jens' code.
4. Finally I have no idea about:

return bless $self, $class;

I looked at http://perldoc.perl.org/functions/bless.html regarding
bless - but I did not understand how $class would refer to the current
package.

See above.
I would also like to know if every file in Perl should end with a 1; -
I did not know this before - but only learnt about it when you used
it.

No. Every file use'd or require'd should return a true value.

Again, reading the relevant documentation that is installed on your
computer would have alerted you to this.

Sinan
Thanks a lot for your code :) I think that's exactly what I wanted.

Yup, another dish of fish served.

Sinan
 
J

Jens Thoms Toerring

O. Olson said:
My Questions:
Can this package be declared in a separate file - I tried it - but it
would not work. I put this in a file called "MyTime.pl" and in the
original source file put "use main::MyTime;". This does not work.

You need to name it 'MyTime.pm' since it's a module. And in the
file that uses it you just put 'use MyTime;', without the 'main::'
in front of it.
sub new {
my $inv = shift;
my $class = ref( $inv ) || $inv;
die "Too many arguments\n" unless @_ <=3;
my $self;
if ( @_ == 0 ) { # no arguments
my @time = localtime( );
$self = { hour => $time[ 2 ],
minute => $time[ 1 ],
second => $time[ 0 ] };
} else {
$self = { hour => $_[ 0 ],
minute => $_[ 1 ] || 0,
second => $_[ 2 ] || 0 };
}
return bless $self, $class;

}
I think I did not understand some of the aspects of this new()
function - though I liked the clever way you created a new hash
everytime a new object of the class is required. Keith above mentioned
that I should do this - but he did not elaborate that way you did it.

It's not "my" clever way, it's copied staight from the Camel
book, "Programming Perl";-)

1. I think from the documentation that "shift" would decrease the size
of the array @_ by 1. So shouldn't that at the top be @_ <=2;

Actually, the new() methid gets called with up to 4 arguments,
the first one being either the name of the package or an
object of the class. When you write

my $a = new MyTime();

(the parenthesis after MyTime are redundant), then you pass
1 argument to the new() method, the name of the class. So,
after the shift, you still should have up to 3 remaining
arguments.
2. I think I did not understand what the second line does:
my $class = ref( $inv ) || $inv;

Since new() is a methid it can be either called with the name
of the class or via an object of that class. The first way
is either

my $a = new MyTime;

or, more verbose,

my $a = MyTime::new( 'MyTime' );

In this case the argument is the name of the class, so it's
not a reference and calling ref() on it returns false. The
other way to call it is as a method (if you already have an
object of that class)

my $b = $a->new;

In this case the method receives the object as it's first
argument (that's basically the 'this' variable from C++)
and ref() returns the name of the class.
3. Is $time sort of an implicit variable.

No, @time is just a simple array that is used locally to this
function. There's nothing "magic" about it.
I looked at the "localtime"
function at http://perldoc.perl.org/functions/localtime.html and I
could not find any mention of it.

localtime() returns an array, which gets assigned to @time.
4. Finally I have no idea about:
return bless $self, $class;

That's what ties it all together. Up to this line $self was
just a simple reference to an anonymous hash. But we want
it to be a bit more, a real object, and that's what the
bless function makes out of it - it makes $self an object
of the class MyTime (and returns the new object which we
then return to the caller. Only because of this you can
use it later with all the object-oriented bells and wistles
like calling

my $string = $a->as_text;

If you would leave out the call of bless you would get a
error message like "Can't call method "new" on unblessed
reference".
I would also like to know if every file in Perl should end with a 1; -
I did not know this before - but only learnt about it when you used
it.

Yes, every package and module needs to end in '1;' (or something
else that indicates success, so e.g. '"a";' would also do).

Regards, Jens
 
O

O. Olson

Thanks Sinan.

I am not very familiar with Perl. I do not use it too often and when
I come back to it after some time I invariably forget a lot of stuff
that I had known before.

I fact, I would prefer not to put packages in the root namespace but
instead adopt the following convention:

package My::Time;

To use this package, suppose your application is in

C:\opt\mytime

I would then put my custom package in c:\opt\mytime\lib\My\Time.pm

That file would start with the package declaration:

package My::Time;

Thanks for this suggestion. I am not yet a professional developer - so
I would simply go with 'MyTime.pm'.

There two schools of thought on being able to call new on the class and
the object. I subscribe to the camp that thinks this is unnecessary.

sub new {
my $class = shift;

I think I agree with you here - because that's how C++/Java do it. But
anyway that new concept by Perl is nice to know.

Thank you for your other comments. I think Jens post clarified my
other questions.

Regards,
O.O.
 
O

O. Olson

Dear Jens,

Thank you for all of your detailed explanations. I think I have got
everything to work now.

It's not "my" clever way, it's copied staight from the Camel
book, "Programming Perl";-)

I found this method described in perltoot - I don't know how I missed
this earlier.

No, @time is just a simple array that is used locally to this
function. There's nothing "magic" about it.

I overlooked the @time variable here. I don't know how this happened.
That's what ties it all together. Up to this line $self was
just a simple reference to an anonymous hash. But we want
it to be a bit more, a real object, and that's what the
bless function makes out of it - it makes $self an object
of the class MyTime (and returns the new object which we
then return to the caller. Only because of this you can
use it later with all the object-oriented bells and wistles
like calling

my $string = $a->as_text;

If you would leave out the call of bless you would get a
error message like "Can't call method "new" on unblessed
reference".

I am totally new to "bless" - this is the first time I would be using
it. Thanks for the explanation.

Yes, every package and module needs to end in '1;' (or something
else that indicates success, so e.g. '"a";' would also do).


I did not know this. I would be using this from now on.

Regards, Jens

I think I have some more questions regarding subroutines and OO
programming in Perl - but I think I would start a new thread for that
as it is a bit unrelated to my current thread.

vielen Danke,
O.O.
 
A

A. Sinan Unur

Thanks Sinan.

You are most welcome.
I am not very familiar with Perl. I do not use it too often and when
I come back to it after some time I invariably forget a lot of stuff
that I had known before.

The ActiveState distribution does contain a fairly effective and very
pretty HTML version of the standard Perl documentation.

It is a good idea to take a look at the table of contents and the FAQ
every time you need to refresh your Perl information.

I usually find the command line easier:

perldoc perldoc
perldoc perltoc
perldoc perlfaq

perldoc -f function_name

perldoc -q faq_keyword

For example:

C:\> perldoc -q object

Found in C:\opt\Perl\lib\pod\perlfaq3.pod

Where can I learn about object-oriented Perl programming?

A good place to start is perltoot, and you can use perlobj, perlboot,
perltoot, perltooc, and perlbot for reference.

A good book on OO on Perl is the "Object-Oriented Perl" by Damian Conway
from Manning Publications, or "Learning Perl References, Objects, &
Modules" by Randal Schwartz and Tom Phoenix from O'Reilly Media.

Sinan
 
O

O. Olson

Sinan,

Thanks for the tips. My problem is that in day-to-day stuff I do not
use Perl. So this means that once I leave it for sometime, I forget
all of the small details (e.g. The first argument passed to a sub-
routine is a reference to the object that called the sub-routine.).
When I return, I have no idea what I have forgotten and I actually
know.

You seem to be a student like myself. How do you handle these things
i.e. we guys have to do C++, Java, Perl etc. in addition to other
software programs like Matlab and others. How do you keep track of
everything i.e. once I leave something for a month I almost forget it.

Regards,
O.O.
 
A

A. Sinan Unur

You seem to be a student like myself.

Well, I am a perpetual student but my official studies ended 24 years
after enrolling in grade school almost eight years ago.
How do you handle these things i.e. we guys have to do C++, Java,
Perl etc. in addition to other

Programming has been a hobby of mine since I was ten and learned Z80
assembly.

I have never formally studied anything related to computer science. I
learned whatever I was interested in and whatever I needed to get stuff
done.

At any one point in time, I only remember a small subset of the most
essential information about each thing/topic I am interested in.
software programs like Matlab and others.

I always look up anything and everything I am going to use. If I am
going to write a C program, I'll have Plauger's Standard C Library book
by my side, I check the man page of each library function I call etc.

When programming Perl, I always look at the documentation first. Perl
has great documentation.

Python does not. Ruby does not. PHP does not. At least, what is
available is not suitable for me and I have avoided those languages.

Java has good API documentation but it is very hard to get a good big
picture. Also, now that I have been spoiled by Perl, I really find it
exhausting to write Java unless absolutely necessary.

The API has improved a lot but I rarely use Java these days because I
find that Perl satisfies write-once / run everywhere much better than
Java (especially with PAR these days).

Before the STL, I found C++ a drag. Not too much benefit, a lot of cost.
With the STL (and Boost), I almost enjoy writing C++.

I don't write platform specific stuff and I haven't written a GUI
program in a long time, so there aren't a lot of APIs to remember.

As for things like MatLab (well, I prefer Octave), when you know what
you want to do, it is very easy to figure out how to do it.

The idea is to first describe what you want to do in words. Then
identify the tasks that need to be accomplished for that, find out if
there are APIs or other modules that help with the building blocks and
then glue everything together.

And, of course, mistakes are made. Mistakes are identified. I find it
harder to forget stuff I learned by making mistakes.
How do you keep track of
everything i.e. once I leave something for a month I almost forget it.

I don't keep track of everything. All non-essential stuff can be looked
up. In my spare time, I read a lot. Some of it sticks.

I tend to read FAQ lists every now and then. I don't necessarily
remember the contents for a long time, but when I hit a snag, something
in the back of my mind leads me to the document which has the solution.

With Perl, I read and participate in this group to learn.

Sinan
 
O

O. Olson

Thanks Sinan for sharing about yourself. I think I am much younger
than you - you seem to have studied longer than I have had years of my
life.

I think that it is just my problem that I cannot remember. I was just
checking if you had some smarter solution :)
Thanks again,
O.O.
 
O

O. Olson

Unfortunitly in perl5 you can't do that. The best you can do is the
following.

sub print_time ($) {
my $temp_time = shift;
print $temp_time;

}

Thanks David.
I am thinking I would start a new thread on this topic - after
reading up what the docs say.

Thanks again,
O.O.
 
D

Dr.Ruud

A. Sinan Unur schreef:
When programming Perl, I always look at the documentation first. Perl
has great documentation.

Python does not. Ruby does not. PHP does not. At least, what is
available is not suitable for me and I have avoided those languages.

PHP is just a tiny language and a lot of functions, so after a short
while, one "just" needs the functions reference, like:
http://www.php.net/manual/en/ref.strings.php

The TOC of the manual is here: http://www.php.net/manual/en/
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top