Re: multiple "sub" packages in 1 file: possible? how?
by GrandFather (Saint) on Aug 05, 2007 at 02:10 UTC
|
You have it almost right, but Perl needs to know which package things are coming from. Here's one way to do it:
use strict;
package my_temp_convert;
my $cur_ctemp=0;
sub set_ctemp {$cur_ctemp = shift;}
sub c {return $cur_ctemp;}
sub f {return 32 + (9 * $cur_ctemp) / 5;}
package main;
my_temp_convert::set_ctemp(32);
printf "%d F is %d C\n", my_temp_convert::f(), my_temp_convert::c();
Prints:
89 F is 32 C
DWIM is Perl's answer to Gödel
| [reply] [d/l] [select] |
|
|
Yeah...I thought about explicit package names before the package names, but I wanted it to be as if I had "Exported" the names from the package (ala @Exports=qw(set_ctemp f c). Then I can use the "Exported" (from the inline package, in this case) names in the main program.
| [reply] |
Re: multiple "sub" packages in 1 file: possible? how?
by naikonta (Curate) on Aug 05, 2007 at 05:20 UTC
|
I'd like to highlight some things:
Firstly, embedding packages in a script, or having multiple packages in the same file are not unusual. One can even use the technique of embedded-package in production for practical reason.
So is there anyway to define a separately named package and not put it in another file?
Yes, exactly as you give in your example. Just put the package PackageName; line to define a new package scope, or switch back to a previously defined package.
#!/usr/bin/perl
use strict;
use warnings;
use CGI;
# this is by default in package main scope
package TempConverter;
# define necessary stuff for TempConverter
# and start its package scope
# you can even do this, to....
package CGI;
# ...extend the CGI package, but this is not
# recommended
package TempConverter;
# back in TempConverter scope and add
# something you need
package main;
# now in main scope, ready for action!
# the safest place to do things
It's important to pay attention about the declaration/definition order to avoid unexpected result.
How do I "use" it in the main scriptlet file?
There are some ways:
- By using fully qualified name on the resources you'd like to use (subroutines & package variables), that is prefixing the symbol name with package name and '::'. As shown by GrandFather, you say my_temp_convert::set_temp(32) instead of set_temp(32).
Aliasing the symbol tables you're going to access so you don't have to fully qualify the symbol name. kyle has shown some examples.
package main;
*set_scale = \&my_temp_convert::set_scale;
set_scale(1);
- Make the package as a class by defining a construct, e.g. new(). kyle also mentions about this.
sub new { bless {} }
sub set_scale {
my $self = shift;
$curr_scale = shift;
}
# later.....
package main;
my $temp = my_temp_convert->new;
$temp->set_scale(1);
- Since you're using Exporter in my_temp_convert package, you can import the symbol tables into main package. (kyle uses an alternative syntax to do this).
package main;
my_temp_convert->import;
set_scale(1)
- Should the my_temp_convert package doesn't use Exporter module, but you still want to access the symbols without fully qualifying their names, and you don't want to alias them one by one, then you can alias them all at once.
package main;
no strict 'refs'; # this is necessary
for my $sym (keys %my_temp_convert::) {
*{"::$sym"} = *{"my_temp_convert::$sym"};
}
set_scale(1);
Note: I remember I once used a single assignment to basically do the same with what for does above, but I can't find references about that in the official docs anymore. The assignment below,
*main:: = *my_temp_convert::;
gives me a "Modification of a read-only value" fatal exception. I'm not sure if that's the correct syntax I used or thing has changed. Any monk knows what happens?
Don't focus on the triviality of my "temperature convert" package
Well, at least use valid syntax even for example. Or, state that your example uses pseudo code. Please remember that we're using PerlMonks to help each other. Supplying some example code indicates that you put some efforts and help others to help you. Things in <code></code> tags are downloadable, and allow other people to try your code. So I would appreciate if you wrote,
sub set_scale{$curr_scale=shift}
sub set_temp($){
#convert to C if needed, and store in "curtmp"
}
instead of
sub set_scale{ curr_scale=shift}
sub set_temp($){#convert to C if needed, and store in "curtmp"}
People will try to work based on your code you supply to closely match the context of your problem so it's prefered if your code is syntatically correct (unless the problem is with the syntax itself), so people don't have to modify a lot of things.
Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!
| [reply] [d/l] [select] |
|
|
package foo;
sub foo {print "bar"};
package main;
%main:: = %foo::;
print "$_ => $main::{$_}" for keys %main::;
foo();
__END__
foo => *foo::foo
Undefined subroutine &main::foo called at -e line 1.
so the shortest way to do the assignment you want is using a "hash slice":
@main::{ keys %foo:: } = values %foo::;
--shmem
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
| [reply] [d/l] [select] |
|
|
Hash slicing will surely do the job. But what I'm talking about is a single line normal assignment, like what I am sure I did in the past. Just like we do with individual symbols,
*alias = *target;
# so we have $alias = $target, @alias = @target
# %alias = %target and so on
but we can do it package-wise.
Assigning the symbol table hash itself doesn't help either (because it clobbers the table)
You are right, shmem. I really forget how I did what I'm trying to say, but I really did. So, until I find my old code, I will consider that it can't be done, and stick with the for loop or hash slicing as shmem shown for this particular task.
Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!
| [reply] [d/l] |
|
|
Will have to play with the various examples you mention.
I didn't want to confuse the issue by posting code that might have other distractions in it that would detract from the concept I wished to understand. I wasn't so much interested in having a specific answer, relative to my current code, but wanted to elicit solutions to the concept that I could mull over.
The current code is just a "mash" of trying some new stuff out...experimenting...GUI usage...etc.
Sorry about the syntax errors in my example -- I wasn't intending it to be code -- just a descriptive example of what I was trying to do. FYIW, my script has no temperature functionality in it. It has to do with a simple script to display images via Tk, in a shuffled or random order where each image has a numeric rating that determines its chances of being selected -- while allowing me to change the ratings of one or more images, move backwards and forwards in the "random" sequence...etc. Just a toy, really, but a good experimentation ground for better coding & design...if you know what I mean...
Linda
| [reply] |
|
|
package my::foo;
BEGIN { $INC{'my/foo.pm'} ||= 'dev/null'; }
require exporter; # etc.
package main;
use my::foo;
The addition to %INC tells use/require that the package is already in memory, so it won't try to load it again.
| [reply] [d/l] |
Re: multiple "sub" packages in 1 file: possible? how?
by kyle (Abbot) on Aug 05, 2007 at 02:25 UTC
|
I really wouldn't recommend using packages this way. However, if you're intent on doing it this way, this seems to work:
use strict;
use warnings;
use Test::More 'no_plan';
package my_temp_convert;
my $curr_scale=0; #centigrade, 1=faren
my $cur_ctemp=0;
sub set_scale{ $curr_scale=shift}
sub set_temp{ $cur_ctemp =shift}
sub c {return $cur_ctemp;}
sub f {return 32+(9*$cur_ctemp)/5}
package main;
*set_scale = \&my_temp_convert::set_scale;
*set_temp = \&my_temp_convert::set_temp;
*c = \&my_temp_convert::c;
*f = \&my_temp_convert::f;
set_scale(1);
set_temp(0);
is( f(), 32, 'f() returns 32' );
is( c(), 0, 'c() returns 0' );
You might also want to look at Re^3: Beyond Inside-Out (details), which has some interesting examples.
Generally, I'd advise using a real object for this kind of job. Then you'd do:
my $temp = My::Temp::Thing->new();
$temp->set_c(0);
is( $temp->get_f(), 32, '0 c is 32 f' );
In that case, you don't clutter your current name space with junk from another package. You can have multiple temperature objects instead of just One. Etc. | [reply] [d/l] [select] |
|
|
As I mentioned, using the "package" construct within same file was a "development" step before potentially splitting it off into another file.
The objectification of the items in the packages is _desirable_,
but I haven't gotten to the point of figuring out what things I want to "objectify".
It's a case of I first developed a "script" (if you can call something around 500+ lines 'a script' :-)) -- that started as "hacks" (my first GUI using Tk; hope to convert to native Win32 if/when I get things properly abstracted and objectified). So your advice to restructure using objects is desirable, though possibly premature -- I'm trying to "evolve" the program toward something more modular and clean. Starting from an example that displays a single "picture", I've been evolving/modifying the program from there.
From the large glob of "prog" I have now, I want to move to objects where it makes sense, but first I want to "conceptually" break apart "functions" by putting groups of functions into separate packages.
Perhaps unrealistically idealistic, I'd like to be able to accomplish:
...prog...
use moda;
use modb;
....
where instead of putting the code for "moda" and "modb" in separate files, I can include it "inline". Ideally, I'd like to use the standard "Export" type functions within the defined packages, to somehow specify the functions I want to use as interface functions.
I notice you are using the "*func=\&mypackage::func" notation to import specific names from the packages.
Is there anyway to do that using the standard "Exporter" and "@Exports=" functionality? I.e. pretty much as if I literally copied a normal "package" into my file -- maintains its own namespace, but uses "Exports" to define the exported interface.
If it is not possible, at least I could use "manual" mode of "exporting" my functions from the packages into the main symbol table via the "*func=..." construct. Seemingly not as elegant as using the standard "Exporter" functions, but perhaps the best alternative.
Thanks for the suggestions...at least I can use the above temporarily until I find something better...:-)
| [reply] [d/l] |
|
|
Well, this works too, if you really want to go through Exporter. I don't think you're going to be able to use as you would if the package were in a separate file since use really implies other fileness.
use strict;
use warnings;
use Test::More 'no_plan';
package my_temp_convert;
require Exporter;
our @ISA = ('Exporter');
our @EXPORT = qw(set_temp set_scale c f);
my $curr_scale=0; #centigrade, 1=faren
my $cur_ctemp=0;
sub set_scale{ $curr_scale=shift}
sub set_temp{ $cur_ctemp =shift}
sub c {return $cur_ctemp;}
sub f {return 32+(9*$cur_ctemp)/5}
package main;
Exporter::import( 'my_temp_convert' );
set_scale(1);
set_temp(0);
is( f(), 32, 'f() returns 32' );
is( c(), 0, 'c() returns 0' );
| [reply] [d/l] [select] |
|
|
|
|
|
Re: multiple "sub" packages in 1 file: possible? how?
by FunkyMonk (Bishop) on Aug 05, 2007 at 08:56 UTC
|
I find it's a very useful technique to have packages and tests in a single file while developing...
BEGIN {
package Foo;
...
}
use Test::More 'no_plan';
#use_ok 'Foo';
...
| [reply] [d/l] [select] |